本文概览:解析Sentinel通过session实现单实例登录流程;通过redis代替session保存登录信息,从而实现分布式登录。
1 session与sessionId
当用户第一次访问服务端时,服务端创建一个session,以<sessionId,session>键值对的方式保存到服务端缓存中,并把sessionId通过cookie方式发送给客户端。后续客户端发送请求,都会把这个sessionId传给服务端,服务端从cookie中获取到这个sessionId,然后根据sessionId从服务器缓存(类似一个Map<sessionId,session>)中获取session。
2 通过session实现单实例登录
以sentinel的源码为例:
1、第一次登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@PostMapping("/login") public Result<AuthService.AuthUser> login(HttpServletRequest request, String username, String password) { if (StringUtils.isNotBlank(DashboardConfig.getAuthUsername())) { authUsername = DashboardConfig.getAuthUsername(); } if (StringUtils.isNotBlank(DashboardConfig.getAuthPassword())) { authPassword = DashboardConfig.getAuthPassword(); } // 登录验证 if (StringUtils.isNotBlank(authUsername) && !authUsername.equals(username) || StringUtils.isNotBlank(authPassword) && !authPassword.equals(password)) { LOGGER.error("Login failed: Invalid username or password, username=" + username); return Result.ofFail(-1, "Invalid username or password"); } AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); // 登录验证通过之后,保存用户到session中 request.getSession().setAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY, authUser); return Result.ofSuccess(authUser); } |
2、登录完成之后,对请求进行鉴权.
通过获取session中用户信息来判断是否已经登录:存在用户信息,表示已经登录,否则未登录。
1 2 3 4 5 6 7 |
public Result<?> check(HttpServletRequest request) { AuthService.AuthUser authUser = authService.getAuthUser(request); if (authUser == null) { return Result.ofFail(-1, "Not logged in"); } return Result.ofSuccess(authUser); } |
通过authService.getAuthUser从session中获取用户信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> { public static final String WEB_SESSION_KEY = "session_sentinel_admin"; @Override public AuthUser getAuthUser(HttpServletRequest request) { HttpSession session = request.getSession(); Object sentinelUserObj = session.getAttribute(SimpleWebAuthServiceImpl.WEB_SESSION_KEY); if (sentinelUserObj != null && sentinelUserObj instanceof AuthUser) { return (AuthUser) sentinelUserObj; } return null; } ..... } |
3、退出登录。通过session的invalidate清空sesssion。
1 2 3 4 5 |
@PostMapping(value = "/logout") public Result<?> logout(HttpServletRequest request, HttpServletResponse response) { request.getSession().invalidate(); return Result.ofSuccess(null); } |
3 通过Redis实现分布式session
通过redis代替session来保存用户登录信息。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
@Service public class RedisSessionRepository { private static final String COOKIE_KEY = "APPNAME_LOGIN_IDENTITY."; @Autowired private JedisClient jedisClient; // 保存所有客户度服务的信息。哈希存储 private static String SESSION_KEY = "sentinel.auth."; public void save(String sessionId, AuthService.AuthUser authUser) { if (null != authUser) { jedisClient.set(generateSessionKey(sessionId), JSON.toJSONString(authUser)); jedisClient.expire(generateSessionKey(sessionId), 3600 * 2); } } public AuthService.AuthUser getAuthUser(String sessionId) { String value = cjedisClient.get(generateSessionKey(sessionId)); if (StringUtils.isNotBlank(value)) { return JSONObject.parseObject(value, SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl.class); } else { return null; } } public void delete(String sessionId) { jedisClient.del(generateSessionKey(sessionId)); } /** * 从request中获取sessionId */ public String getSessionIdFromCookie(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(generateCookieKey())) { return cookie.getValue(); } } } return ""; } /** * 新建sessionId * * @param response * @return */ public String createSessionidFromCookie(HttpServletResponse response) { String sessionId = UUID.randomUUID().toString(); Cookie cookie = new Cookie(COOKIE_KEY, sessionId); cookie.setPath("/"); cookie.setMaxAge(60 * 60 * 24 * 3); response.addCookie(cookie); return sessionId; } private String generateSessionKey(String sessionId) { StringBuilder key = new StringBuilder(SESSION_KEY); key.append(sessionId); return key.toString(); } } |
1、登录接口
1 2 3 4 5 6 7 8 9 |
@PostMapping("/login") public Result<AuthService.AuthUser> login(HttpServletRequest request, String username, String password) { .... AuthService.AuthUser authUser = new SimpleWebAuthServiceImpl.SimpleWebAuthUserImpl(username); // 登录验证通过之后,保存用户到session中 String sessionId= redisSessionRepository.createSessionidFromCookie(request); redisSessionRepository.save(sessionId,authUser) return Result.ofSuccess(authUser); } |
2、登录验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SimpleWebAuthServiceImpl implements AuthService<HttpServletRequest> { public static final String WEB_SESSION_KEY = "session_sentinel_admin"; @Override public AuthUser getAuthUser(HttpServletRequest request) { String sessionId= redisSessionRepository.createSessionidFromCookie(request); AuthService.AuthUser auhtUser = redisSessionRepository.getAuthUser(sessionId); if (auhtUser != null) { return authUser; } return null; } ..... } |
3、退出登录
1 2 3 4 5 6 |
@PostMapping(value = "/logout") public Result<?> logout(HttpServletRequest request, HttpServletResponse response) { String sessionId= redisSessionRepository.createSessionidFromCookie(request); redisSessionRepository.delete(sessionId); return Result.ofSuccess(null); } |