分布式session
如果我们的服务部署到了多台的服务器上面,那么我们的请求可能是落在不同的机器上的
这个时候如何获取到这个用户的session呢,这就要我们的分布式session来解决问题了.
解决分布式session可以采用 现有的容器 来进行这个session的同步。
这里我们就介绍不适用容器我们自己进行分布式session的处理
实现思想
在用户登录系统成功之后呢,给这个用户的session生成一个token,然后用这个token作为key将session存入到redis中
并且将token返回给客户端存储在cookie中
用户之后的请求都将携带这个token,传入给服务器,服务器使用token取出用户的session信息
从而达到分布式session的处理
生成token的方法
使用java的工具类 UUID
UUID.randomUUID()
这样我们的token有了
登录处理
假设我们的登录信息在这个 LoginVo
LoginVo
public class LoginVo {
@NotNull
@Mobile
private String mobile;
@NotNull
@Length(min = 32)
private String password;
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "LoginVo [mobile=" + mobile + ", password=" + password + "]";
}
}
接着我们上节内容,UserService中加入如下的方法。
下面的代码是接续着我前面的spring-boot系列的。
UserService
public boolean login(HttpServletResponse response, LoginVo loginVo);
public User getByToken(HttpServletResponse response, String token) ;
void addCookie(HttpServletResponse response, String token, User user)
addCookie实现
现将 user存入到redis中
然后生成 Cookie
然后将Cookie 加入到response
public void addCookie(HttpServletResponse response, String token, User user) throws Exception {
RedisServiceProxy proxy = new RedisServiceProxy(UserKey.TOKEN,this.redisService);
proxy.setBean(token, user);
Cookie cookie = new Cookie(ConstValue.COOKI_NAME_TOKEN, token);
cookie.setMaxAge(UserKey.TOKEN.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
login实现
判断用户是否存在
用户存在,生成token
调用
addCookie
getByToken实现
使用token从redis中获取到用户信息
如果获取到信息,那么延长cookie的时间
UserService.java
package miaosha.service;
import javax.servlet.http.HttpServletResponse;
import miaosha.dao.domain.User;
import miaosha.vo.login.LoginVo;
public interface UserService {
public User findUserById(Long id) ;
/**
* 根据session的token获取到user
* @param response
* @param token
* @return
* @throws Exception
*/
public User getByToken(HttpServletResponse response, String token) throws Exception ;
public int insert(User user);
public boolean login(HttpServletResponse response, LoginVo loginVo) throws Exception ;
void addCookie(HttpServletResponse response, String token, User user) throws Exception;
}
UserServiceImpl.java
package miaosha.service.impl;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import miaosha.dao.UserDao;
import miaosha.dao.domain.User;
import miaosha.exception.ApplicationException;
import miaosha.redis.key.space.UserKey;
import miaosha.redis.service.RedisService;
import miaosha.redis.service.proxy.RedisServiceProxy;
import miaosha.service.UserService;
import miaosha.util.ConstValue;
import miaosha.util.Md5Util;
import miaosha.util.UUIDUtil;
import miaosha.vo.login.LoginVo;
/**
* service
*
* @author kaifeng1
*
*/
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
RedisService redisService;
public User findUserById(Long id) {
return this.userDao.findUserById(id);
}
@Transactional
public int insert(User user) {
return this.userDao.insert(user);
}
/**
* 登录系统
* @throws Exception
*/
public boolean login(HttpServletResponse response, LoginVo loginVo) throws Exception {
if (loginVo == null) {
throw ApplicationException.SERVER_ERROR;
}
String mobile = loginVo.getMobile();
String formPass = loginVo.getPassword();
User user = this.findUserById(Long.parseLong(mobile));
if (user == null) {
throw ApplicationException.MOBILE_NOT_EXIST;
}
// 验证密码
String dbPass = user.getPassword();
String saltDB = user.getSalt();
String calcPass = Md5Util.formPassToDBPass(formPass, saltDB);
if (!calcPass.equals(dbPass)) {
throw ApplicationException.PASSWORD_ERROR;
}
// 生成cookie
String token = UUIDUtil.uuid();
addCookie(response, token, user);
return true;
}
/**
* 将用户保存在redis中
*/
public void addCookie(HttpServletResponse response, String token, User user) throws Exception {
RedisServiceProxy proxy = new RedisServiceProxy(UserKey.TOKEN,this.redisService);
proxy.setBean(token, user);
Cookie cookie = new Cookie(ConstValue.COOKI_NAME_TOKEN, token);
cookie.setMaxAge(UserKey.TOKEN.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
public User getByToken(HttpServletResponse response, String token) throws Exception {
if (StringUtils.isEmpty(token)) {
return null;
}
RedisServiceProxy proxy = new RedisServiceProxy(UserKey.TOKEN,this.redisService);
User user = proxy.getBean(token, User.class);
if (user != null) {
//延长cookie
addCookie(response, token, user);
}
return user;
}
}
Controller中获取token
一旦上面的login成功之后,那么cookie就保存到了response中了
下一次的请求来的时候,这个cookie就成了request的内容了
因此我们可以从 request中获取到这个cookie的
我们可以使用 @CookieValue 来获取
可以使用@RequestParam获取
例如下面的方法:
@RequestMapping("/toListOldSample")
public String toListOldSample(HttpServletResponse response,Model model,@CookieValue(value=ConstValue.COOKI_NAME_TOKEN,required=false) String cookieUUid,@RequestParam(value=ConstValue.COOKI_NAME_TOKEN , required=false) String paramUuid) throws Exception {
String uuid = null ;
if(cookieUUid != null ) {
uuid = cookieUUid ;
} else if(paramUuid != null) {
uuid = paramUuid ;
} else {
//uuid丢失了重新到登录页
return "login";
}
User user = this.userService.getByToken(response, uuid);
model.addAttribute("user", user);
return "goods_list";
}
上面这种获取的缺陷
使用上面这种获取的方法是可以拿到token的,然后我们可以查询到客户信息的
但是呢在客户登录系统的这段时间内,每一个请求都要求客户的信息的
我们不能给每一个方法都增加上面的那种请求参数吧?
而且上面那个方法内部 是 耦合了 这个 如何通过token获取到客户信息的详细的逻辑的
一旦这里的逻辑改动了,那么影响的范围还是比较大的。
那么如何处理这种问题呢?
我们知道,controller的方法我们是可以 自由的增加 Request,Response,Model的
为什么我们只要设定了这个参数,就是有值的呢?
肯定是框架传给我们的,框架是如何做的呢?
我们看下节的介绍.