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.utils; 017 018import com.jfinal.core.Controller; 019import com.jfinal.kit.Base64Kit; 020import com.jfinal.kit.HashKit; 021import com.jfinal.kit.LogKit; 022import com.jfinal.kit.StrKit; 023import com.jfinal.log.Log; 024import io.jboot.Jboot; 025import io.jboot.exception.JbootIllegalConfigException; 026import io.jboot.web.JbootWebConfig; 027 028import javax.servlet.http.Cookie; 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpServletResponse; 031import java.math.BigInteger; 032 033/** 034 * 参考:spring-security 035 * https://github.com/spring-projects/spring-security/ 036 * blob/master/web/src/main/java/org/springframework/security/ 037 * web/authentication/rememberme/TokenBasedRememberMeServices.java 038 * ....AbstractRememberMeServices.java 039 * <p> 040 * 加密的cookie工具类 041 */ 042public class CookieUtil { 043 private static final Log LOG = Log.getLog(CookieUtil.class); 044 045 private final static String COOKIE_SEPARATOR = "#"; 046 047 // cookie 加密秘钥 048 private static String COOKIE_ENCRYPT_KEY = JbootWebConfig.getInstance().getCookieEncryptKey(); 049 050 // 2 days(单位:秒) 051 private static int COOKIE_MAX_AGE = JbootWebConfig.getInstance().getCookieMaxAge(); 052 053 // 默认的路径, null 默认路径 "/" 054 private static String defaultPath = null; 055 056 // 默认的域名, null 为当前域名 057 private static String defaultDomain = null; 058 059 060 /** 061 * 在使用之前,小调用此方法进行加密key的设置 062 * 063 * @param key 064 */ 065 public static void initEncryptKey(String key) { 066 COOKIE_ENCRYPT_KEY = key; 067 } 068 069 070 public static String getEncryptKey() { 071 return COOKIE_ENCRYPT_KEY; 072 } 073 074 /** 075 * 设置 默认的 Cookie 有效时间,单位:秒 076 * 077 * @param seconds 078 */ 079 public static void initDefaultCookieMaxAge(int seconds) { 080 COOKIE_MAX_AGE = seconds; 081 } 082 083 public static int getDefaultCookieMaxAge() { 084 return COOKIE_MAX_AGE; 085 } 086 087 088 public static String getDefaultPath() { 089 return defaultPath; 090 } 091 092 public static void setDefaultPath(String defaultPath) { 093 CookieUtil.defaultPath = defaultPath; 094 } 095 096 public static String getDefaultDomain() { 097 return defaultDomain; 098 } 099 100 public static void setDefaultDomain(String defaultDomain) { 101 CookieUtil.defaultDomain = defaultDomain; 102 } 103 104 public static void put(Controller ctr, String key, Object value) { 105 put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); 106 } 107 108 public static void put(HttpServletResponse response, String key, Object value) { 109 put(response, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); 110 } 111 112 public static void put(HttpServletResponse response, String key, Object value, int maxAgeInSeconds) { 113 put(response, key, value, maxAgeInSeconds, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); 114 } 115 116 117 public static void put(Controller ctr, String key, Object value, String secretKey) { 118 put(ctr, key, value, COOKIE_MAX_AGE, defaultPath, defaultDomain, secretKey); 119 } 120 121 122 public static void put(Controller ctr, String key, String value, int maxAgeInSeconds) { 123 put(ctr, key, value, maxAgeInSeconds, defaultPath, defaultDomain, COOKIE_ENCRYPT_KEY); 124 } 125 126 public static void put(Controller ctr, String key, String value, int maxAgeInSeconds, String secretKey) { 127 put(ctr, key, value, maxAgeInSeconds, defaultPath, defaultDomain, secretKey); 128 } 129 130 131 public static void put(Controller ctr, String key, Object value, int maxAgeInSeconds, String path, String domain, String secretKey) { 132 put(ctr.getResponse(), key, value, maxAgeInSeconds, path, domain, secretKey); 133 } 134 135 public static void put(HttpServletResponse response, String key, Object value, int maxAgeInSeconds, String path, String domain, String secretKey) { 136 String cookie = buildCookieValue(value.toString(), maxAgeInSeconds, secretKey); 137 doSetCookie(response, key, cookie, maxAgeInSeconds, path, domain, true); 138 } 139 140 public static void remove(Controller ctr, String key) { 141 doSetCookie(ctr.getResponse(), key, null, 0, null, null, null); 142 } 143 144 public static void remove(HttpServletResponse response, String key) { 145 doSetCookie(response, key, null, 0, null, null, null); 146 } 147 148 public static void remove(Controller ctr, String key, String path, String domain) { 149 doSetCookie(ctr.getResponse(), key, null, 0, path, domain, null); 150 } 151 152 public static String get(Controller ctr, String key) { 153 return get(ctr, key, COOKIE_ENCRYPT_KEY); 154 } 155 156 public static String get(HttpServletRequest request, String key) { 157 return get(request, key, COOKIE_ENCRYPT_KEY); 158 } 159 160 public static String get(Controller ctr, String key, String secretKey) { 161 return get(ctr.getRequest(), key, secretKey); 162 } 163 164 public static String get(HttpServletRequest request, String key, String secretKey) { 165 String cookieValue = getCookie(request, key, null); 166 167 if (cookieValue == null) { 168 return null; 169 } 170 171 try { 172 String value = new String(Base64Kit.decode(cookieValue)); 173 return getFromCookieInfo(secretKey, value); 174 } 175 176 //倘若 cookie 被人为修改的情况下能会出现异常情况 177 catch (Exception ex) { 178 LogKit.error(ex.toString(), ex); 179 } 180 181 return null; 182 } 183 184 185 public static String buildCookieValue(String value, int maxAgeInSeconds, String secretKey) { 186 long saveTime = System.currentTimeMillis(); 187 String encryptValue = encrypt(secretKey, saveTime, maxAgeInSeconds, value); 188 189 StringBuilder stringBuilder = new StringBuilder(); 190 stringBuilder.append(encryptValue); 191 stringBuilder.append(COOKIE_SEPARATOR); 192 stringBuilder.append(saveTime); 193 stringBuilder.append(COOKIE_SEPARATOR); 194 stringBuilder.append(maxAgeInSeconds); 195 stringBuilder.append(COOKIE_SEPARATOR); 196 stringBuilder.append(Base64Kit.encode(value)); 197 198 return Base64Kit.encode(stringBuilder.toString()); 199 } 200 201 private static String encrypt(String secretKey, Object saveTime, Object maxAgeInSeconds, String value) { 202 203 // 若使用了默认的加密秘钥 204 if (JbootWebConfig.DEFAULT_COOKIE_ENCRYPT_KEY.equals(secretKey)) { 205 206 if (Jboot.isDevMode()) { 207 LOG.warn("Warn!!! encrypt key is defalut value. please config \"jboot.web.cookieEncryptKey = xxx\" in jboot.properties"); 208 } 209 // 生产环境下,直接抛出错误! 210 else { 211 throw new JbootIllegalConfigException("Error!!! Cookie encrypt key is not configured. please config \"jboot.web.cookieEncryptKey = xxx\" in jboot.properties"); 212 } 213 } 214 return HashKit.md5(secretKey + saveTime.toString() + maxAgeInSeconds.toString() + value); 215 } 216 217 218 public static String getFromCookieInfo(String secretKey, String cookieValue) { 219 if (StrUtil.isBlank(cookieValue)) { 220 return null; 221 } 222 223 String[] cookieStrings = cookieValue.split(COOKIE_SEPARATOR); 224 if (cookieStrings.length != 4) { 225 return null; 226 } 227 228 String encryptValue = cookieStrings[0]; 229 String saveTime = cookieStrings[1]; 230 String maxAgeInSeconds = cookieStrings[2]; 231 String value = Base64Kit.decodeToStr(cookieStrings[3]); 232 233 String encrypt = encrypt(secretKey, Long.valueOf(saveTime), maxAgeInSeconds, value); 234 235 // 非常重要,确保 cookie 不被人为修改 236 if (!encrypt.equals(encryptValue)) { 237 return null; 238 } 239 240 long maxTimeMillis = Long.parseLong(maxAgeInSeconds) * 1000; 241 242 //可能设置的时间为 0 或者 -1 243 if (maxTimeMillis <= 0) { 244 return value; 245 } 246 247 long saveTimeMillis = Long.parseLong(saveTime); 248 // 查看是否过时 249 if ((saveTimeMillis + maxTimeMillis) - System.currentTimeMillis() > 0) { 250 return value; 251 } 252 //已经超时了 253 else { 254 return null; 255 } 256 } 257 258 public static Long getLong(Controller ctr, String key) { 259 String value = get(ctr, key); 260 return null == value ? null : Long.parseLong(value); 261 } 262 263 public static long getLong(Controller ctr, String key, long defalut) { 264 String value = get(ctr, key); 265 return null == value ? defalut : Long.parseLong(value); 266 } 267 268 public static Integer getInt(Controller ctr, String key) { 269 String value = get(ctr, key); 270 return null == value ? null : Integer.parseInt(value); 271 } 272 273 public static int getInt(Controller ctr, String key, int defalut) { 274 String value = get(ctr, key); 275 return null == value ? defalut : Integer.parseInt(value); 276 } 277 278 public static BigInteger getBigInteger(Controller ctr, String key) { 279 String value = get(ctr, key); 280 return null == value ? null : new BigInteger(value); 281 } 282 283 public static BigInteger getBigInteger(Controller ctr, String key, BigInteger defalut) { 284 String value = get(ctr, key); 285 return null == value ? defalut : new BigInteger(value); 286 } 287 288 289 private static String getCookie(HttpServletRequest request, String name, String defaultValue) { 290 Cookie cookie = getCookieObject(request, name); 291 return cookie != null ? cookie.getValue() : defaultValue; 292 } 293 294 /** 295 * Get cookie object by cookie name. 296 */ 297 private static Cookie getCookieObject(HttpServletRequest request, String name) { 298 Cookie[] cookies = request.getCookies(); 299 if (cookies != null) { 300 for (Cookie cookie : cookies) { 301 if (cookie.getName().equals(name)) { 302 return cookie; 303 } 304 } 305 } 306 return null; 307 } 308 309 310 private static void doSetCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds, String path, String domain, Boolean isHttpOnly) { 311 Cookie cookie = new Cookie(name, value); 312 cookie.setMaxAge(maxAgeInSeconds); 313 // set the default path value to "/" 314 if (StrKit.isBlank(path)) { 315 path = "/"; 316 } 317 cookie.setPath(path); 318 319 if (domain != null) { 320 cookie.setDomain(domain); 321 } 322 if (isHttpOnly != null) { 323 cookie.setHttpOnly(isHttpOnly); 324 } 325 response.addCookie(cookie); 326 } 327 328 329}