用户登录
登录需求分析
1:获取账号跟密码
2:通过账号密码查询数据库获取用户对象user
3:如果用户存在,表示登录成功
4:使用UUID创建出随机的唯一的token值
String token = ....
5:以token'为key,user为value 缓存到redis中
6:将token跟用户对象使用json格式放回浏览器
浏览器:
接受到返回值,解析出token跟用户信息,并缓存
前端发起请求是,获取浏览器缓存的token值,通过请求头的方式携带到后端服务器
接口2:
1>通过请求对象获取前端传过来token
String token =req.getHeader("token")
2>以token为key,查Redis缓存库得到用户对象user
3>判断user是否为null
----------------------------------------------------------------------
Session方式:
4:将user对象缓存到session中
服务器:
1>系统马上创建cookie对象
Cookie c =new Cookie("jsessionid",xxx)
2>使用response对象将cookie回传倒浏览器
response.addCookie(c)
浏览器:
1>浏览器接受cookie之后会缓存cookie
2>第二次请求时,浏览器会自动将cookie携带到后端
接口2:
1:通过请求对象获取session对象
1>请求对象获取cookie对象,然后解析出jsessionid
2>再通过jsessionid找到之前创建session对象
2:通过session对象获取之前缓存的用户对象
令牌登录方式流程(token)
1:通过请求获取username和password,查询数据库得到用户对象user
2:通过user对象判断user是否为null,如果为null提示,如果不为null表示登录成功
3:通过uuid方式创建token令牌(要求:随机,唯一)
String token=UUID().randomUUID()...
4:以token作为key,用户对象user作为value缓存到redis数据库中,token的有效时间设置为30分钟
5:将token跟user对象使用json格式返回浏览器
6:浏览器接受并解析响应数据得到token更user对象,缓存到cookie(此处的cookie也设置有效时间是30分钟)
---------------------------------------------------------------------------------------
7:第二次访问接口时,前端发送请求时,将浏览器缓存token数据添加到请求头中,跟请求参数一起携带到服务器
8:服务器通过请求对象,调用String token =req.getHeader("token"),得到前端传过来的token数据
9:以token为key查询redis缓存库,得到user对象
10:判断user对象是否为null,决定当前是否已经登录
1>如果user为null,表示没有登录
2>如果user不为null,表示已经登录,登录成功后,此时必须重置token有效时间,不重置的话,过30分钟又要重新登录,用户的体验会非常差
UserController
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping("/login")
public Object login(String username, String password) {
UserInfo userInfo = userInfoService.login(username, password);
String token = userInfoRedisService.setToken(userInfo);
HashMap<String, Object> map = new HashMap<>();
map.put("token", token);
map.put("user", userInfo);
return JsonResult.success(map);
}
@RequireLogin
@GetMapping("/currentUser")
public Object currentUser(HttpServletRequest request) {
String token = request.getHeader("token");
UserInfo userInfo = userInfoRedisService.getUserByToken(token);
return JsonResult.success(userInfo);
}
}
UserInfoServiceImpl(验证用户名和密码)
@Service
public class UserInfoServiceImpl implements IUserInfoService {
public UserInfo login(String username, String password) {
UserInfo userInfo = userInfoRepository.findByPhone(username);
if (userInfo == null || !userInfo.getPassword().equals(password)) {
throw new LogicException("用户名或密码错误");
}
userInfo.setPassword("");
return userInfo;
}
}
UserInfoRedisServiceImpl(使用redis存取用户信息)
@Service
public class UserInfoRedisServiceImpl implements IUserInfoRedisService {
@Autowired
private StringRedisTemplate template;
@Override
public String setToken(UserInfo userInfo) {
String token = UUID.randomUUID().toString().replaceAll("-", "");
String key = RedisKeys.USER_LOGIN_TOKEN.join(token);
String value = JSON.toJSONString(userInfo);
template.opsForValue().set(key, value, USER_LOGIN_TOKEN.getTime(), TimeUnit.SECONDS);
return token;
}
-------------------------------------------------------------------------------------
@Override
public UserInfo getUserByToken(String token) {
if (!StringUtils.hasLength(token)) {
return null;
}
String key = RedisKeys.USER_LOGIN_TOKEN.join(token);
if (template.hasKey(key)) {
String userStr = template.opsForValue().get(key);
UserInfo userInfo = JSON.parseObject(userStr, UserInfo.class);
template.expire(key, RedisKeys.USER_LOGIN_TOKEN.getTime(), TimeUnit.SECONDS);
return userInfo;
}
return null;
}
}
redis中key键设计善于枚举类
@Getter
public enum RedisKeys {
VERIFY_CODE("verify_code", Consts.VERIFY_CODE_VAI_TIME * 60L),
USER_LOGIN_TOKEN("user_login_token", Consts.USER_INFO_TOKEN_VAI_TIME * 60L);
private String prefix;
private Long time;
private RedisKeys(String prefix, Long time) {
this.prefix = prefix;
this.time = time;
}
public String join(String... values) {
StringBuilder sb = new StringBuilder();
sb.append(prefix);
for (String value : values) {
sb.append(":").append(value);
}
return sb.toString();
}
}