1. 程式人生 > >SpringBoot使用redis做分散式Session

SpringBoot使用redis做分散式Session

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";
        }
    }
}