001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.web.session; 017 018import io.jboot.Jboot; 019import io.jboot.components.cache.JbootCache; 020import io.jboot.components.cache.JbootCacheManager; 021 022import javax.servlet.http.*; 023import java.util.Enumeration; 024import java.util.HashMap; 025import java.util.Map; 026import java.util.UUID; 027 028 029public class JbootServletRequestWrapper extends HttpServletRequestWrapper { 030 031 private static JbootSessionConfig config = Jboot.config(JbootSessionConfig.class); 032 033 private static int maxInactiveInterval = config.getMaxInactiveInterval(); 034 private static String cookieName = config.getCookieName(); 035 private static String cookiePath = config.getCookieContextPath(); 036 private static String cookieDomain = config.getCookieDomain(); 037 private static int cookieMaxAge = config.getCookieMaxAge(); 038 private static String cacheName = config.getCacheName(); 039 private static String useCacheName = config.getUseCacheName(); 040 041 private static JbootCache jbootCache = JbootCacheManager.me() 042 .getCache(useCacheName); 043 044 045 private HttpServletResponse response; 046 private HttpServletRequest originRequest; 047 private HttpSession originSession; 048 private JbootHttpSession jbootSession; 049 050 051 public JbootServletRequestWrapper(HttpServletRequest request, HttpServletResponse response) { 052 super(request); 053 this.response = response; 054 this.originRequest = request; 055 this.originSession = request.getSession(false); 056 } 057 058 @Override 059 public HttpSession getSession() { 060 return getSession(true); 061 } 062 063 064 @Override 065 public HttpSession getSession(boolean create) { 066 if (jbootSession != null) { 067 return jbootSession; 068 } 069 070 String sessionId = getCookie(cookieName); 071 if (sessionId != null) { 072 jbootSession = new JbootHttpSession(sessionId, originRequest.getServletContext(), createSessionStore(sessionId), originSession); 073 jbootSession.setMaxInactiveInterval(maxInactiveInterval); 074 } else if (create || originSession != null) { 075 sessionId = UUID.randomUUID().toString().replace("-", ""); 076 jbootSession = new JbootHttpSession(sessionId, originRequest.getServletContext(), createSessionStore(sessionId), originSession); 077 jbootSession.setMaxInactiveInterval(maxInactiveInterval); 078 setCookie(cookieName, sessionId, cookieMaxAge); 079 } 080 081 return jbootSession; 082 } 083 084 private Map<String, Object> createSessionStore(String sessionId) { 085 Map<String, Object> store = jbootCache.get(cacheName, sessionId); 086 if (store == null) { 087 store = new HashMap<>(); 088 syncOriginSessionData(store); 089 jbootCache.put(cacheName, sessionId, store); 090 } 091 return store; 092 } 093 094 095 /** 096 * 同步上层 session 到 sessionStore 097 * @// TODO: 2021/6/3 若上层动态修改了 上层自己的 session,会导致 Controller 的 session 和 上层 session 不同步的情况 098 * @// TODO: 2021/6/3 临时的解决方案需要用户手动通过 Controller 来修改 session 数据 099 * @param store 100 */ 101 private void syncOriginSessionData(Map<String, Object> store) { 102 if (this.originSession != null) { 103 Enumeration<String> names = originSession.getAttributeNames(); 104 while (names.hasMoreElements()) { 105 String name = names.nextElement(); 106 store.put(name, originSession.getAttribute(name)); 107 } 108 } 109 } 110 111 112 /** 113 * http请求结束时,更新session信息,包括:刷新session的存储时间,更新session数据,清空session数据等 114 */ 115 public void refreshSession() { 116 if (jbootSession == null) { 117 return; 118 } 119 120 //session 已经被整体删除,用户调用了session.invalidate() 121 if (!jbootSession.isValid()) { 122 jbootCache.remove(cacheName, jbootSession.getId()); 123 setCookie(cookieName, null, 0); 124 } 125 126 //session 已经被修改(session数据的增删改查) 127 else if (jbootSession.isDataChanged()) { 128 Map<String, Object> snapshot = jbootSession.snapshot(); 129 // 数据已经全部被删除了 130 if (snapshot.isEmpty()) { 131 jbootCache.remove(cacheName, jbootSession.getId()); 132 setCookie(cookieName, null, 0); 133 } else { 134 jbootCache.put(cacheName, jbootSession.getId(), snapshot, maxInactiveInterval); 135 } 136 } 137 138 //更新session存储时间 139 else { 140 jbootCache.setTtl(cacheName, jbootSession.getId(), maxInactiveInterval); 141 } 142 } 143 144 145 /** 146 * Get cookie value by cookie name. 147 */ 148 private String getCookie(String name) { 149 Cookie cookie = getCookieObject(name); 150 return cookie != null ? cookie.getValue() : null; 151 } 152 153 /** 154 * Get cookie object by cookie name. 155 */ 156 private Cookie getCookieObject(String name) { 157 Cookie[] cookies = originRequest.getCookies(); 158 if (cookies != null) { 159 for (Cookie cookie : cookies) { 160 if (cookie.getName().equals(name)) { 161 return cookie; 162 } 163 } 164 } 165 return null; 166 } 167 168 /** 169 * @param name 170 * @param value 171 * @param maxAgeInSeconds 172 */ 173 private void setCookie(String name, String value, int maxAgeInSeconds) { 174 if (!response.isCommitted()) { 175 Cookie cookie = new Cookie(name, value); 176 cookie.setMaxAge(maxAgeInSeconds); 177 cookie.setPath(cookiePath); 178 if (cookieDomain != null) { 179 cookie.setDomain(cookieDomain); 180 } 181 cookie.setHttpOnly(true); 182 response.addCookie(cookie); 183 } 184 } 185 186 187 public HttpServletRequest getOriginRequest() { 188 return originRequest; 189 } 190}