package com. kongjs. emo. web. session ; import javax. servlet. ServletContext ;
import javax. servlet. http. HttpSession ;
import javax. servlet. http. HttpSessionContext ;
import java. util. * ;
public class Session implements HttpSession { private String id; private final long creationTime; private int maxInactiveInterval; private long lastAccessedTime; private final ServletContext servletContext; private Map < String , Object > attributes; private boolean isNew; private long responseEndTime; public Session ( ServletContext servletContext, String id) { long now = System . currentTimeMillis ( ) ; this . id = id; this . creationTime = now; this . maxInactiveInterval = servletContext. getSessionTimeout ( ) * 60 ; this . lastAccessedTime = now; this . servletContext = servletContext; this . attributes = new HashMap < > ( ) ; this . isNew = true ; this . responseEndTime = now; } @Override public long getCreationTime ( ) { return this . creationTime; } @Override public String getId ( ) { return this . id; } @Override public long getLastAccessedTime ( ) { return this . lastAccessedTime; } @Override public ServletContext getServletContext ( ) { return this . servletContext; } @Override public void setMaxInactiveInterval ( int i) { this . maxInactiveInterval = i; } @Override public int getMaxInactiveInterval ( ) { return this . maxInactiveInterval; } @SuppressWarnings ( value = "deprecated" ) @Override public HttpSessionContext getSessionContext ( ) { throw new RuntimeException ( "不支持" ) ; } @Override public Object getAttribute ( String name) { return this . attributes. get ( name) ; } @SuppressWarnings ( value = "deprecated" ) @Override public Object getValue ( String name) { return this . attributes. get ( null ) ; } @Override public Enumeration < String > getAttributeNames ( ) { return Collections . enumeration ( this . attributes. keySet ( ) ) ; } @SuppressWarnings ( value = "deprecated" ) @Override public String [ ] getValueNames ( ) { return this . attributes. keySet ( ) . toArray ( new String [ 0 ] ) ; } @Override public void setAttribute ( String name, Object value) { this . attributes. put ( name, value) ; } @SuppressWarnings ( value = "deprecated" ) @Override public void putValue ( String name, Object value) { this . attributes. put ( name, value) ; } @Override public void removeAttribute ( String name) { this . attributes. remove ( name) ; } @Override public void removeValue ( String name) { this . attributes. remove ( name) ; } @Override public void invalidate ( ) { this . attributes. clear ( ) ; } @Override public boolean isNew ( ) { return this . isNew; } public void setId ( String id) { this . id = id; } public void setLastAccessedTime ( long lastAccessedTime) { this . lastAccessedTime = lastAccessedTime; this . isNew = false ; } public void setResponseEndTime ( long responseEndTime) { this . responseEndTime = responseEndTime; } public long getResponseEndTime ( ) { return this . responseEndTime; } public Map < String , Object > getAttributes ( ) { return this . attributes; } public boolean isExpired ( ) { return ( System . currentTimeMillis ( ) - this . creationTime) / 1000 >= this . maxInactiveInterval; } @Override public String toString ( ) { return this . toMap ( ) . toString ( ) ; } public Map < String , Object > toMap ( ) { Map < String , Object > map = new LinkedHashMap < > ( ) ; map. put ( "id" , this . id) ; map. put ( "creationTime" , this . creationTime) ; map. put ( "maxInactiveInterval" , this . maxInactiveInterval) ; map. put ( "lastAccessedTime" , this . lastAccessedTime) ; map. put ( "responseEndTime" , this . responseEndTime) ; map. put ( "isNew" , this . isNew) ; map. put ( "isExpired" , this . isExpired ( ) ) ; map. put ( "attributes" , this . attributes) ; return map; }
}
package com. kongjs. emo. web. session. wrapper ; import com. kongjs. emo. web. session. Session ;
import com. kongjs. emo. web. session. manager. SessionManager ; import javax. servlet. ServletContext ;
import javax. servlet. SessionCookieConfig ;
import javax. servlet. http. * ;
import java. time. Instant ;
import java. time. ZoneId ;
import java. time. ZoneOffset ;
import java. time. ZonedDateTime ;
import java. time. format. DateTimeFormatter ;
public class RequestSessionWrapper extends HttpServletRequestWrapper { private final HttpServletRequest request; private final HttpServletResponse response; private final SessionManager sessionManager; private String requestedSessionId; private boolean requestedSessionCookie; private boolean requestedSessionURL; public RequestSessionWrapper ( HttpServletRequest request, HttpServletResponse response, SessionManager sessionManager) { super ( request) ; this . request = request; this . response = response; this . sessionManager = sessionManager; } @Override public HttpServletRequest getRequest ( ) { return request; } public HttpServletResponse getResponse ( ) { return response; } public SessionManager getSessionManager ( ) { return sessionManager; } private String readCookieValue ( ServletContext servletContext) { SessionCookieConfig cookieConfig = servletContext. getSessionCookieConfig ( ) ; if ( this . getCookies ( ) != null ) { for ( Cookie cookie : this . getCookies ( ) ) { if ( cookie. getName ( ) . equals ( cookieConfig. getName ( ) ) ) { this . requestedSessionCookie = true ; this . requestedSessionURL = false ; return cookie. getValue ( ) ; } } } String id = this . getRequest ( ) . getParameter ( cookieConfig. getName ( ) ) ; if ( id != null && id. length ( ) > 0 ) { this . requestedSessionCookie = false ; this . requestedSessionURL = true ; return id; } return null ; } private void writeCookieValue ( ServletContext servletContext, String value) { SessionCookieConfig cookieConfig = servletContext. getSessionCookieConfig ( ) ; StringBuilder sb = new StringBuilder ( ) ; sb. append ( cookieConfig. getName ( ) ) . append ( '=' ) ; if ( value != null && value. length ( ) > 0 ) { sb. append ( value) ; } int maxAge = cookieConfig. getMaxAge ( ) ; if ( maxAge > - 1 ) { sb. append ( "; Max-Age=" ) . append ( maxAge) ; ZonedDateTime expires = ( maxAge != 0 ) ? ZonedDateTime . now ( ZoneId . systemDefault ( ) ) . plusSeconds ( maxAge) : Instant . EPOCH . atZone ( ZoneOffset . UTC ) ; sb. append ( "; Expires=" ) . append ( expires. format ( DateTimeFormatter . RFC_1123_DATE_TIME ) ) ; } String domain = cookieConfig. getDomain ( ) ; if ( domain != null && domain. length ( ) > 0 ) { sb. append ( "; Domain=" ) . append ( domain) ; } String path = cookieConfig. getPath ( ) ; if ( path != null && path. length ( ) > 0 ) { sb. append ( "; Path=" ) . append ( path) ; } sb. append ( "; HttpOnly" ) ; sb. append ( "; SameSite=" ) . append ( "Lax" ) ; response. addHeader ( "Set-Cookie" , sb. toString ( ) ) ; } private HttpSession newSession ( ) { Session session = this . getSessionManager ( ) . createSession ( this . getServletContext ( ) ) ; this . getSessionManager ( ) . save ( session) ; this . writeCookieValue ( this . getServletContext ( ) , session. getId ( ) ) ; this . requestedSessionId = session. getId ( ) ; return session; } @Override public HttpSession getSession ( boolean create) { String authorization = this . getRequest ( ) . getHeader ( "Authorization" ) ; String id = this . readCookieValue ( this . getServletContext ( ) ) ; if ( id != null ) { Session session = this . getSessionManager ( ) . findById ( id) ; if ( session != null ) { if ( session. isExpired ( ) ) { this . sessionManager. deleteById ( id) ; HttpSession httpSession = this . newSession ( ) ; this . requestedSessionId = httpSession. getId ( ) ; return httpSession; } session. setLastAccessedTime ( System . currentTimeMillis ( ) ) ; this . getSessionManager ( ) . save ( session) ; this . requestedSessionId = id; return session; } } if ( create) { HttpSession httpSession = this . newSession ( ) ; this . requestedSessionId = httpSession. getId ( ) ; return httpSession; } return null ; } @Override public HttpSession getSession ( ) { return this . getSession ( true ) ; } @Override public String getRequestedSessionId ( ) { return this . requestedSessionId; } @Override public String changeSessionId ( ) { HttpSession httpSession = this . getSession ( ) ; Session session = ( Session ) httpSession; session. setId ( this . getSessionManager ( ) . idGenerator ( ) ) ; this . getSessionManager ( ) . save ( session) ; this . requestedSessionId = session. getId ( ) ; return session. getId ( ) ; } @Override public boolean isRequestedSessionIdValid ( ) { return this . requestedSessionId != null ; } @Override public boolean isRequestedSessionIdFromCookie ( ) { return this . requestedSessionCookie; } @Override public boolean isRequestedSessionIdFromURL ( ) { return this . requestedSessionURL; } @Override public boolean isRequestedSessionIdFromUrl ( ) { return this . isRequestedSessionIdFromURL ( ) ; }
}
package com. kongjs. emo. web. session. manager ; import cn. hutool. core. util. IdUtil ;
import com. kongjs. emo. web. session. Session ;
import com. kongjs. emo. web. session. manager. SessionManager ;
import org. springframework. stereotype. Component ; import javax. servlet. ServletContext ;
import java. util. ArrayList ;
import java. util. List ;
import java. util. Map ;
import java. util. UUID ;
import java. util. concurrent. ConcurrentHashMap ;
import java. util. stream. Collectors ;
public interface SessionManager { String idGenerator ( ) ; Session createSession ( ServletContext servletContext) ; void save ( Session session) ; Session findById ( String id) ; Session deleteById ( String id) ; List < Session > findAll ( ) ; List < Session > findByAttributeEquals ( String name, String value) ; List < Session > findByAttributeContain ( String name, String value) ;
}
@Component
public class MapSessionManager implements SessionManager { private final Map < String , Session > sessionMap = new ConcurrentHashMap < > ( ) ; @Override public String idGenerator ( ) { return IdUtil . getSnowflakeNextIdStr ( ) ; } @Override public Session createSession ( ServletContext servletContext) { return new Session ( servletContext, idGenerator ( ) ) ; } @Override public void save ( Session session) { this . sessionMap. put ( session. getId ( ) , session) ; } @Override public Session findById ( String id) { return this . sessionMap. get ( id) ; } @Override public Session deleteById ( String id) { return this . sessionMap. remove ( id) ; } @Override public List < Session > findAll ( ) { return new ArrayList < > ( this . sessionMap. values ( ) ) ; } @Override public List < Session > findByAttributeEquals ( String name, String value) { return this . sessionMap. values ( ) . stream ( ) . filter ( m -> m. getAttribute ( name) != null && m. getAttribute ( name) . equals ( value) ) . collect ( Collectors . toList ( ) ) ; } @Override public List < Session > findByAttributeContain ( String name, String value) { return this . sessionMap. values ( ) . stream ( ) . filter ( m -> m. getAttribute ( name) != null && m. getAttribute ( name) . toString ( ) . contains ( value) ) . collect ( Collectors . toList ( ) ) ; }
}
package com. kongjs. emo. web. session. filter ; import com. kongjs. emo. web. session. Session ;
import com. kongjs. emo. web. session. wrapper. RequestSessionWrapper ;
import com. kongjs. emo. web. session. wrapper. ResponseSessionWrapper ;
import com. kongjs. emo. web. session. manager. SessionManager ;
import org. springframework. core. annotation. Order ;
import org. springframework. stereotype. Component ; import javax. servlet. FilterChain ;
import javax. servlet. ServletException ;
import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. io. IOException ; @Order ( SessionFilter . DEFAULT_ORDER )
@Component
public class SessionFilter extends OneFilter { public static final int DEFAULT_ORDER = Integer . MIN_VALUE + 50 ; private final SessionManager sessionManager; public SessionFilter ( SessionManager sessionManager) { this . sessionManager = sessionManager; }
@Override protected void doFilterInternal ( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException , ServletException { final RequestSessionWrapper requestSessionWrapper = new RequestSessionWrapper ( request, response, sessionManager) ; final ResponseSessionWrapper responseSessionWrapper = new ResponseSessionWrapper ( request, response) ; Session session = ( Session ) requestSessionWrapper. getSession ( false ) ; try { filterChain. doFilter ( requestSessionWrapper, responseSessionWrapper) ; } finally { if ( session != null ) { session. setResponseEndTime ( System . currentTimeMillis ( ) ) ; } } }
}
package com. kongjs. emo. web. controller ; import com. kongjs. emo. web. session. Session ;
import org. springframework. web. bind. annotation. RequestMapping ;
import org. springframework. web. bind. annotation. RestController ; import javax. servlet. http. HttpServletRequest ;
@RestController
@RequestMapping ( "/session" )
public class SessionController { @RequestMapping ( "/info" ) public Object session ( HttpServletRequest request) { Session session = ( Session ) request. getSession ( ) ; return session. toMap ( ) ; }
}