SpringBoot使用redis做分散式Session
阿新 • • 發佈:2018-12-26
1. 核心原理
主要是通過redis,當用戶登入後生成一個隨機的uuid作為token,將token作為鍵,user物件作為值儲存到redis資料庫中,同時將token儲存到cookie中 ,當訪問其他頁面時判斷cookie中是否有token,如果有,則根據此token從redis拿到使用者資訊即可。
2. User服務類
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
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);
}
具體實現類
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. 使用
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";
}
}
}