0.前言
这里省略介绍redis的基本概念,和使用redis储存用户登录信息的好处。
原料:已经引入redis的java项目,(框架无所谓,我这里是springboot)
1.几个初始的步骤
1.1 确定好前台传输的,请求头上的登录标识
这里我使用的是“sessionId”
1.2 定义储存用户信息的类,方便redis做存储
@Data
public class SessionUser implements Serializable {
//session用户数据的类型(默认为 A,SessionTypeUtil)
private String sessionType;
//用户id
private Long userId;
//手机号
private String phone;
//名字
private String name;
}
1.3 初始化redis的方法,这里只需要用到最基础的 get、set方法
2.确定本次登录架构的整体逻辑
一次请求的代码逻辑拓扑图
3.代码逻辑的编写
3.1 用户信息储存到redis(登录接口)
@GetMapping(value = "/login", produces = "application/json;charset=UTF-8")
@ApiOperation(value = "手机号登录", httpMethod = "GET", produces = "application/json;charset=UTF-8")
@ApiResponse(code = 200, message = "Ok", response = Result.class)
@ApiImplicitParams({
@ApiImplicitParam(name = "phone", value = "phone", required = true, paramType = "query"),
@ApiImplicitParam(name = "password", value = "password", required = true, paramType = "query")
})
public Result<Object> login(String phone, String password) {
//注意:这里只做简单的登录校验,删除了验证码及密码加密的逻辑
//mybatisPlus的写法
Wrapper<UserIndex> wrapper = new EntityWrapper<>();
wrapper.eq("phone", phone);
wrapper.eq("password", password);
List<UserIndex> userIndices = userIndexService.selectList(wrapper);
if (userIndices.size() == 1) {
UserIndex userIndex = userIndices.get(0);
SessionUser sessionUser = new SessionUser();
sessionUser.setUserId(userIndex.getId());
sessionUser.setPhone(userIndex.getPhone());
sessionUser.setName(userIndex.getName());
sessionUser.setSessionType(SessionTypeUtil.INDEX_USER);
String sessionId = SessionIdUtil.getSessionId();
//登录时 redis存储一个sessionId 和 用户的注册信息
sessionManager.setSession(sessionId, sessionUser);
logger.info(sessionUser.getPhone() + ":登录成功,sessionId:" + sessionId);
return response("登录成功,sessionId:" + sessionId);
} else {
throw new Exception("手机号或密码错误!");
}
}
3.2 接口获取用户信息的方式
从以上的拓扑图可以看出,当用户成功登录,前端得到用户的登录标识(sessionId)后,我们就能通过指定的代码逻辑获取用户的登录信息,并返回用户想要的内容。
经分析,得出因为每个接口都要面对进行登录的校验,所以我们把拓扑图的实现代码放在Filter中最为合适。
@Slf4j
@Component
public class SessionFilter implements Filter {
/**
* 这里只是写了 doFilter 的方法实现
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String requestURI = ((HttpServletRequest) request).getRequestURI();
String sessionId = req.getHeader("sessionId"); //获取sessionId
try {
SessionUser sessionUser;
if (是否为不用登录的接口) {
//1.是 不用登录的接口
chain.doFilter(request, response);
return;
} else {
//2.不是 需要登录
try {
//查询登录信息
sessionUser = getSessionUser(sessionId);//(获取用户信息的方法,获取不到抛出异常)
//延长sessionTime
sessionManager.setSession(sessionId, sessionUser);
} catch (Exception e) {
//登录信息获取失败 getSessionUser(抛出异常了)
if (是否为非强制登录的接口) {
//是非强制登录的接口
sessionUser = null;
} else {
throw e;
}
}
}
//将取出的用户信息写入当前线程
sessionManager.setCurrent(sessionUser);
//先进行接口中数据逻辑的运行(中间应该会拿取线程中的登录信息)
chain.doFilter(request, response);
//再把 当前线程存储的用户信息清空
sessionManager.clear();
} catch (SessionException e) {
String code = 502;
log.debug(String.format("sessionId: %s, msg: %s", sessionId, e.getMessage()));
res.setContentType("application/json;charset=UTF-8");
res.setHeader("Access-Control-Allow-Origin", "*");
res.getWriter().write(JSON.toJSONString(new Result<>(code, e.getMessage(), "")));
res.getWriter().close();
return;
}
}
}
3.3 使用用户数据
至此我们已经将获取到的用户信息写入本次线程中,等到接口中要使用用户信息就从当前线程中取就行。
3.4 注意事项
(1)因为将用户信息写进了线程,故退出登录时一定记得清除当前线程中的用户信息。
(2)为了便于扩展,我们在储存用户信息的类中预留了sessionType字段,对于不太复杂的系统,可以尝试用一个sessionUser类存储前后台,多个账户类型的信息。