SpringBoot使用redis做分布式Session

1. 核心原理

主要是通过redis,当用户登录后生成一个随机的uuid作为token,将token作为键,user对象作为值存储到redis数据库中,同时将token保存到cookie中 ,当访问其他页面时判断cookie中是否有token,如果有,则根据此token从redis拿到用户信息即可。

关于springboot 操作redis方面请查看 : https://blog.csdn.net/junmoxi/article/details/80694405

2. User服务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.pibigstar.springboot.service.impl;

import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.pibigstar.springboot.dao.JPADao;
import com.pibigstar.springboot.domain.User;
import com.pibigstar.springboot.service.RedisService;
import com.pibigstar.springboot.service.UserService;

@Service
public class UserServiceImpl implements UserService{

public static final String COOKIE_USER_TOKEN = "token";
public static final int COOKIE_TOKEN_TIMEOUT = 3600*24*2;//两天

@Autowired
private RedisService redisService;

/**
* 登录
* @param response
* @param loginVo
* @return
*/
@Override
public boolean login(HttpServletResponse response, LoginVo loginVo) {
if(loginVo == null) {
throw new GlobalException(CodeMsg.SERVER_ERROR);
}
String mobile = loginVo.getMobile();
String formPass = loginVo.getPassword();
//判断手机号是否存在
User user = getById(Long.parseLong(mobile));
if(user == null) {
throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
}
//验证密码
String dbPass = user.getPassword();
String saltDB = user.getSalt();
String calcPass = MD5Util.formPassToDBPass(formPass, saltDB);
if(!calcPass.equals(dbPass)) {
throw new GlobalException(CodeMsg.PASSWORD_ERROR);
}
//生成cookie
String token = UUID.randomUUID().toString().replace("-", "");
addCookie(response, token, user);
return true;
}

/**
* 根据token从redis中拿到user对象
*/
@Override
public User getByToken(HttpServletResponse response, String token) {
if (token==null) {
return null;
}
User user = (User) redisService.getObject(token);
if (user!=null) {
//延长cookie有效期
addCookie(response,token,user);
}
return user;

}

/**
* 将token放到cookie中
* @param response
* @param token
* @param user
*/
private void addCookie(HttpServletResponse response, String token, User user) {
redisService.setObject(token, user,COOKIE_TOKEN_TIMEOUT);
Cookie cookie = new Cookie(COOKIE_USER_TOKEN, token);
//设置cookie有效期
cookie.setMaxAge(COOKIE_TOKEN_TIMEOUT);
cookie.setPath("/");
response.addCookie(cookie);
}
}

3. 读取cookie中的token

我们通过实现 HandlerMethodArgumentResolver 接口,重写里面的方法,将cookie中或者parameter中的token数据拿到然后查询出user注入到Controller中 的方法参数中

注册一个ArgumentResolvers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.pibigstar.springboot.config;

import java.util.List;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer{
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new UserArgumentResolver());
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}

具体实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.pibigstar.springboot.config;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.thymeleaf.util.StringUtils;

import com.pibigstar.springboot.domain.User;
import com.pibigstar.springboot.service.UserService;
import com.pibigstar.springboot.service.impl.UserServiceImpl;
/**
* 拿到session或parameter中的token
* 通过token拿到redis中的user
* 将user注入到方法的参数中
* @author pibigstar
*
*/
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver{

@Autowired
private UserService userService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> parameterType = parameter.getParameterType();
return parameterType==User.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 cookieToken = getCookieToken(request,UserServiceImpl.COOKIE_USER_TOKEN);
String parameterToken = request.getParameter(UserServiceImpl.COOKIE_USER_TOKEN);

if (StringUtils.isEmpty(parameterToken) && StringUtils.isEmpty(cookieToken)) {
return null;
}
String token = StringUtils.isEmpty(parameterToken)?cookieToken:parameterToken;

return userService.getByToken(response,token);
}

private String getCookieToken(HttpServletRequest request,String cookieName) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals(cookieName)) {
return cookie.getValue();
}
}
return null;
}

}

4. 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.pibigstar.springboot.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

import com.pibigstar.springboot.domain.User;

@Controller
public class UserController {

/**
* 这里的user 是通过token从redis拿到的
* 具体实现在UserArgumentResolver中
*/
public String index(User user,Model model) {
if(user==null) {
return "login";
}else {
model.addAttribute("user",user);
return "index";
}
}
}
-------------本文结束感谢您的阅读-------------