spring security 5 session管理
spring security 如何保存我们的登录信息。
根据官方文档我们可以知道通过登录验证(AuthenticationProvider.authenticate(Authentication authentication)方法中验证,返回一个Authentication
->UsernamePasswordAuthenticationToken(userDetails, password, authorities);)后,会将一个验证通过的实例Authentication
放入安全上下文SecurityContext
,SecurityContext
可从SecurityContextHolder中获得。
Authentication
信息包括:
- principal -验证用户,通常为UserDetails实例(实际生产中实现该接口,通过数据库查询该信息)。
- credentials -通常是密码。在许多情况下,将在验证用户身份后清除此内容,以确保它不会泄漏。
- authorities -权限GrantedAuthority。 通常是角色或范围。
验证通过后,后续需要登录用户信息可直接从Authentication
获取。
spring security 如何记录登录状态
我们知道javaweb 通过jsession 会话id来确保同一个会话,同一个连接。在security中同样通过jSessionId来记录用户登录,每次请求到达服务端,会通过ConcurrentSessionFilter
过滤器验证是否存在该session,不存在则不是登录状态。验证通过继续执行FilterChain
后续的过滤器。
ConcurrentSessionFilter
部分源码
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
HttpSession session = request.getSession(false);
if (session != null) {
SessionInformation info = this.sessionRegistry.getSessionInformation(session.getId());
if (info != null) {
//session过期做退出登录处理
if (info.isExpired()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Requested session ID " + request.getRequestedSessionId() + " has expired.");
}
this.doLogout(request, response);
this.sessionInformationExpiredStrategy.onExpiredSessionDetected(new SessionInformationExpiredEvent(info, request, response));
return;
}
//若该session存在,则刷新它的最后请求时间
this.sessionRegistry.refreshLastRequest(info.getSessionId());
}
}
//若该session不存在,后续处理中会做未登录处理
chain.doFilter(request, response);
}
SessionManagementFilter
部分源码
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
//判断是否被该过滤器处理过
if (request.getAttribute("__spring_security_session_mgmt_filter_applied") != null) {
chain.doFilter(request, response);
} else {
request.setAttribute("__spring_security_session_mgmt_filter_applied", Boolean.TRUE);
if (!this.securityContextRepository.containsContext(request)) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && !this.trustResolver.isAnonymous(authentication)) {
try {
this.sessionAuthenticationStrategy.onAuthentication(authentication, request, response);
} catch (SessionAuthenticationException var8) {
this.logger.debug("SessionAuthenticationStrategy rejected the authentication object", var8);
SecurityContextHolder.clearContext();
this.failureHandler.onAuthenticationFailure(request, response, var8);
return;
}
this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
} else if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Requested session ID " + request.getRequestedSessionId() + " is invalid.");
}
if (this.invalidSessionStrategy != null) {
this.invalidSessionStrategy.onInvalidSessionDetected(request, response);
return;
}
}
}
chain.doFilter(request, response);
}
}
退出登录或删除cookie后的请求security如何处理
- 请求中未携带 jSessionId时只能请求公共资源,无法访问私有资源。(security 定义)
WebSecurityConfigurerAdapter
继承类中定义
@Override
public void configure(WebSecurity web) throws Exception {
//指定多个公共资源,无需登录即可访问
web.ignoring().antMatchers("/login.html", "/page/login/**");
}
- 退出登录后的请求会继续验证session是否存在,不存在则重新登录。