用户登录信息如何保存
总体思路
上一篇《cookie 和 session》中介绍过使用cookie和session保存用户登录信息存在的问题,以及目前主流的解决方案–使用数据库来保存用户的登录信息。那么如何在保证访问速度和安全的情况下保存用户的登录信息呢?我们可以在数据库保存用户的登录信息,登录信息中存有随机生成的用户登录的凭证,一旦用户成功登录之后,我们将这个随机生成的凭证通过Cookie传给浏览器,下次浏览器再次向服务器发送请求的时候,就会将凭证带上一起发给服务器,服务器通过检查这个凭证,就可以得到用户的登录信息。为了计解决并发情况下的同步问题,使用ThreadLocal来保存用户的信息,当请求结束之后把用户信息给移除掉即可。
-
用户登录信息的表设计:
字段说明:
(1)user_id:用户的ID,可以通过这个Id去用户表查到该用户的详细信息,以便显示用户的头像信息。
(2)ticket:用户登录的凭证,是一个随机生成的UUID,用户第一次登录之后会通过Cookie传到浏览器本地保存,之后用户发送请求时带上这个凭证。
(3)status:用户的登录状态,0表示正常登录,1表示退出登录状态。
(4)expired:这次登录的有效日期。 -
登录代码的实现:
public Map<String,Object> login(String username,String password,int expiredSeconds){
Map<String,Object> map = new HashMap<>();
if(StringUtils.isBlank(username)){
map.put("usernameMsg","账号不能为空!");
return map;
}
if(StringUtils.isBlank(password)){
map.put("passwordMsg","密码不能为空!");
return map;
}
User user = userMapper.selectByName(username);
if(user==null){
map.put("usernameMsg","该账号不存在!");
return map;
}
if(user.getStatus()==0){
map.put("usernameMsg","该账号未激活!");
}
if(!CommunityUtil.md5(password + user.getSalt()).equalsIgnoreCase(user.getPassword())){
map.put("passwordMsg","密码不正确!");
return map;
}
LoginTicket loginTicket = new LoginTicket();
loginTicket.setUserId(user.getId());
loginTicket.setTicket(CommunityUtil.generateUUID());
loginTicket.setStatus(0);
loginTicket.setExpired(new Date(System.currentTimeMillis() + 1000 * expiredSeconds));
loginTicketMapper.insertLoginTicket(loginTicket);
map.put("ticket",loginTicket.getTicket()); //在controller层存到Cookie中
return map;
}
- 通过拦截器获取用户的登录信息,从而使得登录成功之后,可以正常访问其他页面。实现逻辑:拦截器拦截除静态资源外的所有请求,然后判断是否带有登录凭证,如果带有登录凭证,那就通过凭证去查询用户的登录信息,如果登录信息正常,那么就通过登录信息查询用户的用户信息,并将用户信息保存在ThreadLocal中,在处理完请求后,将用户信息传给ModelAndView以便前端获取用户信息,当处理完模板以后,将ThreadLocal中的用户信息清除,防止内存泄漏。
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Autowired
HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//从cookie中获取凭证
String ticket = CookieUtil.getValue(request,"ticket");
if(ticket != null){
//查询凭证
LoginTicket loginTicket = userService.findLoginTicket(ticket);
//检查凭证是否有效
if((loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date()))){
//根据凭证查询用户
User user = userService.findUserById(loginTicket.getUserId());
//在本次请求中持有用户信息
if(user != null){
hostHolder.setUsers(user);
}
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
//获取用户信息
User user = hostHolder.getUsers();
//通过ModelAndView把用户信息传给模板
if(user != null && modelAndView != null){
modelAndView.addObject("loginUser",user);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//清除掉用户信息
hostHolder.clear();
}
}
//获取Cookie的工具类
public class CookieUtil {
public static String getValue(HttpServletRequest request,String name){
if(request == null || name == null){
throw new IllegalArgumentException("参数为空!");
}
Cookie[] cookies = request.getCookies();
if(cookies != null){
for(Cookie cookie : cookies){
if(cookie.getName().equals(name)){
return cookie.getValue();
}
}
}
return null;
}
}
//保存用户信息的工具类
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUsers(User user){
users.set(user);
}
public User getUsers(){
return users.get();
}
public void clear(){
users.remove();
}
}
//注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css","/**/*.js","/**/.png","/**/*.jpg","/**/*.jpeg");
}
}