guomengjiao 1 год назад
Родитель
Сommit
4a49419a41

+ 27 - 0
common/src/main/java/com/jeesite/common/constant/Constants.java

@@ -146,4 +146,31 @@ public interface Constants {
         String Packaging_Materials = "packaging-materials";   // 包装材料
         String Agriculture = "agriculture";   // 农业
     }
+
+    /**
+     * 登录用户Token令牌缓存KEY前缀
+     */
+    String PREFIX_USER_TOKEN = "bjfl:user:token:";
+//    /** Token缓存时间:3600秒即一小时 */
+//    int  TOKEN_EXPIRE_TIME  = 3600;
+
+    /**
+     * 缓存登录用户的Token
+     */
+    String PREFIX_USER_TOKEN_LIST = "bjfl:user:token:list:";
+
+    /**
+     * String 类型的空值
+     */
+    String STRING_NULL = "null";
+
+    /**
+     * 符号:下划线
+     */
+    String UNDERLINE = "_";
+
+    /**
+     * 未知的
+     */
+    String UNKNOWN = "unknown";
 }

+ 1 - 0
modules/bjflapi/bjflapi.iml

@@ -202,5 +202,6 @@
     <orderEntry type="module" module-name="jeesite-module-report" />
     <orderEntry type="module" module-name="jeesite-module-basedata" />
     <orderEntry type="library" name="Maven: org.dom4j:dom4j:2.1.3" level="project" />
+    <orderEntry type="library" name="Maven: com.auth0:java-jwt:3.11.0" level="project" />
   </component>
 </module>

+ 109 - 0
modules/bjflapi/src/main/java/com/jeesite/modules/bjflapi/report/WebsiteUserControllerApi.java

@@ -0,0 +1,109 @@
+package com.jeesite.modules.bjflapi.report;
+
+import com.jeesite.common.codec.DesUtils;
+import com.jeesite.common.config.Global;
+import com.jeesite.common.constant.Constants;
+import com.jeesite.common.lang.StringUtils;
+import com.jeesite.common.service.ServiceException;
+import com.jeesite.modules.report.entity.WebsiteUser;
+import com.jeesite.modules.report.service.WebsiteUserService;
+import com.jeesite.modules.report.util.JwtUtil;
+import com.jeesite.modules.report.util.PasswordUtil;
+import com.jeesite.modules.report.util.RedisUtil;
+import com.jeesite.modules.report.util.oConvertUtils;
+import io.swagger.annotations.Api;
+import org.springframework.beans.BeanUtils;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping(value = "${adminPath}/api/report/websiteUser")
+@Api(value = "WebsiteUserControllerApi",tags = "网站用户接口")
+public class WebsiteUserControllerApi {
+    @Resource
+    private RedisUtil redisUtil;
+    @Resource
+    private WebsiteUserService websiteUserService;
+    /**
+     * 登录页面
+     */
+    @PostMapping(value = "login")
+    public Map<String, Object> login(WebsiteUser websiteUser) {
+        String loginCode = websiteUser.getLoginCode();
+        String password = websiteUser.getPassword();
+        // 登录用户名解密(解决登录用户名明文传输安全问题)
+        String secretKey = Global.getProperty("shiro.loginSubmit.secretKey");
+        if (StringUtils.isEmpty(loginCode)) {
+            throw new ServiceException("登录账号为空");
+        }
+        if (StringUtils.isEmpty(password)) {
+            throw new ServiceException("登录密码为空");
+        }
+        loginCode = DesUtils.decode(loginCode, secretKey);
+        password = DesUtils.decode(password, secretKey);
+
+        //查询用户
+        WebsiteUser oldUser = websiteUserService.findLoginCode(loginCode);
+        if (oldUser == null) {
+            throw new ServiceException("用户不存在,请先注册");
+        }
+        //验证密码
+        String passwordEncode = PasswordUtil.encrypt(loginCode, password, oldUser.getSalt());
+        if (!passwordEncode.equals(oldUser.getPassword())) {
+            throw new ServiceException("登录密码不正确");
+        }
+        //TODO 单点登录,清理登录用户的登录token
+        List<Object> tokenList = redisUtil.lGet(Constants.PREFIX_USER_TOKEN_LIST + loginCode, 0, -1);
+        tokenList.forEach(t -> redisUtil.del(Constants.PREFIX_USER_TOKEN + t));
+        redisUtil.del(Constants.PREFIX_USER_TOKEN_LIST + loginCode);
+
+        // 生成token
+        String token = JwtUtil.sign(loginCode, password);
+        // 设置token缓存有效时间
+        redisUtil.set(Constants.PREFIX_USER_TOKEN + token, token);
+        redisUtil.expire(Constants.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
+        //TODO 单点登录,缓存登录用户的Token
+        redisUtil.lSet(Constants.PREFIX_USER_TOKEN_LIST + loginCode, token);
+        Map<String, Object> map = new HashMap<>();
+        map.put("user", oldUser);
+        map.put("token", token);
+        return map;
+    }
+
+    @PostMapping(value = "register")
+    public Map<String, Object> register(WebsiteUser websiteUser) {
+        String loginCode = websiteUser.getLoginCode();
+        String password = websiteUser.getPassword();
+        // 登录用户名解密(解决登录用户名明文传输安全问题)
+        String secretKey = Global.getProperty("shiro.loginSubmit.secretKey");
+        if (StringUtils.isEmpty(loginCode)) {
+            throw new ServiceException("登录账号为空");
+        }
+        if (StringUtils.isEmpty(password)) {
+            throw new ServiceException("登录密码为空");
+        }
+        loginCode = DesUtils.decode(loginCode, secretKey);
+        password = DesUtils.decode(password, secretKey);
+        WebsiteUser oldUser = websiteUserService.findLoginCode(loginCode);
+        if (oldUser != null) {
+            throw new ServiceException("用户已存在");
+        }
+        WebsiteUser newUser = new WebsiteUser();
+        BeanUtils.copyProperties(websiteUser, newUser);
+        newUser.setLoginCode(loginCode);
+        String salt = oConvertUtils.randomGen(8);
+        newUser.setSalt(salt);
+        String passwordEncode = PasswordUtil.encrypt(loginCode, password, salt);
+        newUser.setPassword(passwordEncode);
+
+        websiteUserService.save(newUser);
+        return login(websiteUser);
+    }
+
+}

+ 118 - 0
modules/core/src/main/java/com/jeesite/modules/config/RedisConfig.java

@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * <p>https://www.renren.io
+ *
+ * <p>版权所有,侵权必究!
+ */
+package com.jeesite.modules.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.lang.reflect.Method;
+
+/**
+ * Redis配置
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Configuration
+public class RedisConfig {
+  @Autowired private RedisConnectionFactory factory;
+
+  @Bean
+  public RedisTemplate<String, Object> redisTemplate() {
+    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+    redisTemplate.setKeySerializer(new StringRedisSerializer());
+    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+    redisTemplate.setHashValueSerializer(new StringRedisSerializer());
+    redisTemplate.setValueSerializer(new StringRedisSerializer());
+    redisTemplate.setConnectionFactory(factory);
+    return redisTemplate;
+  }
+
+  /**
+   * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
+   *
+   * @param redisConnectionFactory
+   * @return
+   */
+  @Bean
+  public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
+        new Jackson2JsonRedisSerializer<Object>(Object.class);
+    ObjectMapper om = new ObjectMapper();
+    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+    jackson2JsonRedisSerializer.setObjectMapper(om);
+
+    RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
+    template.setConnectionFactory(redisConnectionFactory);
+
+    // template.setKeySerializer(jackson2JsonRedisSerializer);
+    // 使用StringRedisSerializer来序列化和反序列化redis的key值
+    template.setKeySerializer(new StringRedisSerializer());
+    template.setValueSerializer(new StringRedisSerializer());
+
+    //template.setValueSerializer(jackson2JsonRedisSerializer);
+    template.setHashKeySerializer(new StringRedisSerializer());
+    template.setHashValueSerializer(jackson2JsonRedisSerializer);
+    template.afterPropertiesSet();
+    return template;
+  }
+
+  @Bean(name = "keyGenerator")
+  public KeyGenerator firstParamKeyGenerator() {
+    return new KeyGenerator() {
+      @Override
+      public Object generate(Object target, Method method, Object... params) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(target.getClass().getName());
+        sb.append(method.getName());
+        for (Object obj : params) {
+          sb.append(obj.toString());
+        }
+        return sb.toString();
+      }
+    };
+  }
+
+  @Bean
+  public HashOperations<String, String, Object> hashOperations(
+      RedisTemplate<String, Object> redisTemplate) {
+    return redisTemplate.opsForHash();
+  }
+
+  @Bean
+  public ValueOperations<String, String> valueOperations(
+      RedisTemplate<String, String> redisTemplate) {
+    return redisTemplate.opsForValue();
+  }
+
+  @Bean
+  public ListOperations<String, Object> listOperations(
+      RedisTemplate<String, Object> redisTemplate) {
+    return redisTemplate.opsForList();
+  }
+
+  @Bean
+  public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
+    return redisTemplate.opsForSet();
+  }
+
+  @Bean
+  public ZSetOperations<String, Object> zSetOperations(
+      RedisTemplate<String, Object> redisTemplate) {
+    return redisTemplate.opsForZSet();
+  }
+}

+ 4 - 3
modules/report/jeesite-module-report.iml

@@ -29,7 +29,6 @@
     <orderEntry type="module" module-name="jeesite-common" />
     <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" />
     <orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.10.0" level="project" />
-    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" />
     <orderEntry type="library" name="Maven: commons-io:commons-io:2.11.0" level="project" />
     <orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.9.4" level="project" />
     <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
@@ -38,8 +37,6 @@
     <orderEntry type="library" name="Maven: org.javassist:javassist:3.21.0-GA" level="project" />
     <orderEntry type="library" name="Maven: org.objenesis:objenesis:2.5.1" level="project" />
     <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.13.4" level="project" />
-    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.13.4" level="project" />
-    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.13.4" level="project" />
     <orderEntry type="library" name="Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.13.4" level="project" />
     <orderEntry type="library" name="Maven: org.codehaus.woodstox:stax2-api:4.2.1" level="project" />
     <orderEntry type="library" name="Maven: com.fasterxml.woodstox:woodstox-core:6.3.1" level="project" />
@@ -196,5 +193,9 @@
     <orderEntry type="library" name="Maven: com.github.xiaoymin:knife4j-spring-ui:2.0.9" level="project" />
     <orderEntry type="module" module-name="jeesite-module-basedata" />
     <orderEntry type="library" name="Maven: org.dom4j:dom4j:2.1.3" level="project" />
+    <orderEntry type="library" name="Maven: com.auth0:java-jwt:3.11.0" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.13.4" level="project" />
+    <orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.13.4" level="project" />
+    <orderEntry type="library" name="Maven: commons-codec:commons-codec:1.15" level="project" />
   </component>
 </module>

+ 7 - 0
modules/report/pom.xml

@@ -36,6 +36,13 @@
 			<artifactId>dom4j</artifactId>
 			<version>2.1.3</version>
 		</dependency>
+
+		<!--JWT-->
+		<dependency>
+			<groupId>com.auth0</groupId>
+			<artifactId>java-jwt</artifactId>
+			<version>3.11.0</version>
+		</dependency>
 	</dependencies>
 
 	<developers>

+ 15 - 4
modules/report/src/main/java/com/jeesite/modules/report/entity/WebsiteUser.java

@@ -18,12 +18,13 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
 		@Column(includeEntity=DataEntity.class),
 		@Column(name="login_code", attrName="loginCode", label="登录账号"),
 		@Column(name="password", attrName="password", label="登录密码"),
+		@Column(name="salt", attrName="salt", label="md5密码盐"),
 		@Column(name="name", attrName="name", label="姓名", queryType=QueryType.LIKE),
 		@Column(name="contact", attrName="contact", label="联系方式"),
 		@Column(name="email", attrName="email", label="邮箱"),
 		@Column(name="company_name", attrName="companyName", label="公司名称", queryType=QueryType.LIKE),
 		@Column(name="demand", attrName="demand", label="需求"),
-		@Column(name="addr", attrName="addr", label="联系地址"),
+		@Column(name="addr", attrName="addr", label="公司职务"),
 	}, orderBy="a.update_date DESC"
 )
 public class WebsiteUser extends DataEntity<WebsiteUser> {
@@ -31,12 +32,13 @@ public class WebsiteUser extends DataEntity<WebsiteUser> {
 	private static final long serialVersionUID = 1L;
 	private String loginCode;		// 登录账号
 	private String password;		// 登录密码
+	private String salt;
 	private String name;		// 姓名
 	private String contact;		// 联系方式
 	private String email;		// 邮箱
 	private String companyName;		// 公司名称
 	private String demand;		// 需求
-	private String addr;		// 联系地址
+	private String addr;		// 公司职务
 
 	public WebsiteUser() {
 		this(null);
@@ -65,7 +67,16 @@ public class WebsiteUser extends DataEntity<WebsiteUser> {
 	public void setPassword(String password) {
 		this.password = password;
 	}
-	
+
+	@Size(min=0, max=45, message="md5密码盐不能超过 45 个字符")
+	public String getSalt() {
+		return salt;
+	}
+
+	public void setSalt(String salt) {
+		this.salt = salt;
+	}
+
 	@Size(min=0, max=32, message="姓名长度不能超过 32 个字符")
 	public String getName() {
 		return name;
@@ -111,7 +122,7 @@ public class WebsiteUser extends DataEntity<WebsiteUser> {
 		this.demand = demand;
 	}
 	
-	@Size(min=0, max=128, message="联系地址长度不能超过 128 个字符")
+	@Size(min=0, max=128, message="公司职务长度不能超过 128 个字符")
 	public String getAddr() {
 		return addr;
 	}

+ 11 - 6
modules/report/src/main/java/com/jeesite/modules/report/service/WebsiteUserService.java

@@ -1,13 +1,13 @@
 package com.jeesite.modules.report.service;
 
-import java.util.List;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
 import com.jeesite.common.entity.Page;
 import com.jeesite.common.service.CrudService;
-import com.jeesite.modules.report.entity.WebsiteUser;
 import com.jeesite.modules.report.dao.WebsiteUserDao;
+import com.jeesite.modules.report.entity.WebsiteUser;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
 
 /**
  * 网站用户Service
@@ -77,5 +77,10 @@ public class WebsiteUserService extends CrudService<WebsiteUserDao, WebsiteUser>
 	public void delete(WebsiteUser websiteUser) {
 		super.delete(websiteUser);
 	}
-	
+
+	public WebsiteUser findLoginCode(String loginCode) {
+		WebsiteUser where = new WebsiteUser();
+		where.setLoginCode(loginCode);
+		return dao.getByEntity(where);
+	}
 }

+ 69 - 0
modules/report/src/main/java/com/jeesite/modules/report/util/JwtUtil.java

@@ -0,0 +1,69 @@
+package com.jeesite.modules.report.util;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+
+import java.util.Date;
+
+/**
+ * @Author Scott
+ * @Date 2018-07-12 14:23
+ * @Desc JWT工具类
+ **/
+public class JwtUtil {
+
+	/**Token有效期为1小时(Token在reids中缓存时间为两倍)*/
+	public static final long EXPIRE_TIME = 60 * 60 * 1000;
+
+	/**
+	 * 校验token是否正确
+	 *
+	 * @param token  密钥
+	 * @param secret 用户的密码
+	 * @return 是否正确
+	 */
+	public static boolean verify(String token, String username, String secret) {
+		try {
+			// 根据密码生成JWT效验器
+			Algorithm algorithm = Algorithm.HMAC256(secret);
+			JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();
+			// 效验TOKEN
+			DecodedJWT jwt = verifier.verify(token);
+			return true;
+		} catch (Exception exception) {
+			return false;
+		}
+	}
+
+	/**
+	 * 获得token中的信息无需secret解密也能获得
+	 *
+	 * @return token中包含的用户名
+	 */
+	public static String getUsername(String token) {
+		try {
+			DecodedJWT jwt = JWT.decode(token);
+			return jwt.getClaim("username").asString();
+		} catch (JWTDecodeException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * 生成签名,5min后过期
+	 *
+	 * @param username 用户名
+	 * @param secret   用户的密码
+	 * @return 加密的token
+	 */
+	public static String sign(String username, String secret) {
+		Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
+		Algorithm algorithm = Algorithm.HMAC256(secret);
+		// 附带username信息
+		return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
+
+	}
+}

+ 190 - 0
modules/report/src/main/java/com/jeesite/modules/report/util/PasswordUtil.java

@@ -0,0 +1,190 @@
+package com.jeesite.modules.report.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import java.security.Key;
+import java.security.SecureRandom;
+
+/**
+ * @Description: 密码工具类
+ * @author: jeecg-boot
+ */
+public class PasswordUtil {
+
+	/**
+	 * JAVA6支持以下任意一种算法 PBEWITHMD5ANDDES PBEWITHMD5ANDTRIPLEDES
+	 * PBEWITHSHAANDDESEDE PBEWITHSHA1ANDRC2_40 PBKDF2WITHHMACSHA1
+	 * */
+
+    /**
+     * 定义使用的算法为:PBEWITHMD5andDES算法
+     * 加密算法
+     */
+	public static final String ALGORITHM = "PBEWithMD5AndDES";
+
+    /**
+     * 定义使用的算法为:PBEWITHMD5andDES算法
+     * 密钥
+     */
+	public static final String SALT = "63293188";
+
+	/**
+	 * 定义迭代次数为1000次
+	 */
+	private static final int ITERATIONCOUNT = 1000;
+
+	/**
+	 * 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. 盐长度必须为8字节
+	 *
+	 * @return byte[] 盐值
+	 * */
+	public static byte[] getSalt() throws Exception {
+		// 实例化安全随机数
+		SecureRandom random = new SecureRandom();
+		// 产出盐
+		return random.generateSeed(8);
+	}
+
+	public static byte[] getStaticSalt() {
+		// 产出盐
+		return SALT.getBytes();
+	}
+
+	/**
+	 * 根据PBE密码生成一把密钥
+	 *
+	 * @param password
+	 *            生成密钥时所使用的密码
+	 * @return Key PBE算法密钥
+	 * */
+	private static Key getPbeKey(String password, byte[] salt) {
+		// 实例化使用的算法
+		SecretKeyFactory keyFactory;
+		SecretKey secretKey = null;
+		try {
+			keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
+			// 设置PBE密钥参数
+			PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONCOUNT, 256);
+			// 生成密钥
+			secretKey = keyFactory.generateSecret(keySpec);
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+
+		return secretKey;
+	}
+
+	/**
+	 * 加密明文字符串
+	 *
+	 * @param plaintext
+	 *            待加密的明文字符串
+	 * @param password
+	 *            生成密钥时所使用的密码
+	 * @param salt
+	 *            盐值
+	 * @return 加密后的密文字符串
+	 * @throws Exception
+	 */
+	public static String encrypt(String plaintext, String password, String salt) {
+		byte[] saltBytes = salt.getBytes();
+		Key key = getPbeKey(password, saltBytes);
+		byte[] encipheredData = null;
+		PBEParameterSpec parameterSpec = new PBEParameterSpec(saltBytes, ITERATIONCOUNT);
+		try {
+			Cipher cipher = Cipher.getInstance(ALGORITHM);
+
+			cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
+			//update-begin-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
+			encipheredData = cipher.doFinal(plaintext.getBytes("utf-8"));
+			//update-end-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
+		} catch (Exception e) {
+		}
+		return bytesToHexString(encipheredData);
+	}
+
+	/**
+	 * 解密密文字符串
+	 *
+	 * @param ciphertext
+	 *            待解密的密文字符串
+	 * @param password
+	 *            生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致)
+	 * @param salt
+	 *            盐值(如需解密,该参数需要与加密时使用的一致)
+	 * @return 解密后的明文字符串
+	 * @throws Exception
+	 */
+	public static String decrypt(String ciphertext, String password, String salt) {
+		byte[] saltBytes = salt.getBytes();
+		Key key = getPbeKey(password, saltBytes);
+		byte[] passDec = null;
+		PBEParameterSpec parameterSpec = new PBEParameterSpec(saltBytes, ITERATIONCOUNT);
+		try {
+			Cipher cipher = Cipher.getInstance(ALGORITHM);
+
+			cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
+
+			passDec = cipher.doFinal(hexStringToBytes(ciphertext));
+		}
+
+		catch (Exception e) {
+			// TODO: handle exception
+		}
+		return new String(passDec);
+	}
+
+	/**
+	 * 将字节数组转换为十六进制字符串
+	 *
+	 * @param src
+	 *            字节数组
+	 * @return
+	 */
+	public static String bytesToHexString(byte[] src) {
+		StringBuilder stringBuilder = new StringBuilder("");
+		if (src == null || src.length <= 0) {
+			return null;
+		}
+		for (int i = 0; i < src.length; i++) {
+			int v = src[i] & 0xFF;
+			String hv = Integer.toHexString(v);
+			if (hv.length() < 2) {
+				stringBuilder.append(0);
+			}
+			stringBuilder.append(hv);
+		}
+		return stringBuilder.toString();
+	}
+
+	/**
+	 * 将十六进制字符串转换为字节数组
+	 *
+	 * @param hexString
+	 *            十六进制字符串
+	 * @return
+	 */
+	public static byte[] hexStringToBytes(String hexString) {
+		if (hexString == null || "".equals(hexString)) {
+			return null;
+		}
+		hexString = hexString.toUpperCase();
+		int length = hexString.length() / 2;
+		char[] hexChars = hexString.toCharArray();
+		byte[] d = new byte[length];
+		for (int i = 0; i < length; i++) {
+			int pos = i * 2;
+			d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+		}
+		return d;
+	}
+
+	private static byte charToByte(char c) {
+		return (byte) "0123456789ABCDEF".indexOf(c);
+	}
+
+}

+ 154 - 0
modules/report/src/main/java/com/jeesite/modules/report/util/RedisUtil.java

@@ -0,0 +1,154 @@
+package com.jeesite.modules.report.util;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public final class RedisUtil {
+    @Autowired
+    public RedisTemplate redisTemplate;
+
+    public boolean expire(String key, long time) {
+        try {
+            if (time > 0L) {
+                this.redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+
+            return true;
+        } catch (Exception var5) {
+            var5.printStackTrace();
+            return false;
+        }
+    }
+
+    public long getExpire(String key) {
+        return this.redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    public boolean hasKey(String key) {
+        try {
+            return this.redisTemplate.hasKey(key);
+        } catch (Exception var3) {
+            var3.printStackTrace();
+            return false;
+        }
+    }
+
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                this.redisTemplate.delete(key[0]);
+            } else {
+                this.redisTemplate.delete(Arrays.asList(key));
+            }
+        }
+
+    }
+
+    public Object get(String key) {
+        return key == null ? null : this.redisTemplate.opsForValue().get(key);
+    }
+
+    public boolean set(String key, Object value) {
+        try {
+            this.redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception var4) {
+            var4.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean set(String key, Object value, long time) {
+        try {
+            if (time > 0L) {
+                this.redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                this.set(key, value);
+            }
+
+            return true;
+        } catch (Exception var6) {
+            var6.printStackTrace();
+            return false;
+        }
+    }
+
+    public long incr(String key, long delta) {
+        if (delta < 0L) {
+            throw new RuntimeException("递增因子必须大于0");
+        } else {
+            return this.redisTemplate.opsForValue().increment(key, delta);
+        }
+    }
+
+    public long decr(String key, long delta) {
+        if (delta < 0L) {
+            throw new RuntimeException("递减因子必须大于0");
+        } else {
+            return this.redisTemplate.opsForValue().increment(key, -delta);
+        }
+    }
+
+    public List<Object> lGet(String key, long start, long end) {
+        try {
+            return this.redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception var7) {
+            var7.printStackTrace();
+            return null;
+        }
+    }
+
+    public boolean lSet(String key, Object value) {
+        try {
+            this.redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception var4) {
+            var4.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean lSet(String key, Object value, long time) {
+        try {
+            this.redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0L) {
+                this.expire(key, time);
+            }
+
+            return true;
+        } catch (Exception var6) {
+            var6.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean lSet(String key, List<Object> value) {
+        try {
+            this.redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception var4) {
+            var4.printStackTrace();
+            return false;
+        }
+    }
+
+    public boolean lSet(String key, List<Object> value, long time) {
+        try {
+            this.redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0L) {
+                this.expire(key, time);
+            }
+
+            return true;
+        } catch (Exception var6) {
+            var6.printStackTrace();
+            return false;
+        }
+    }
+}

+ 642 - 0
modules/report/src/main/java/com/jeesite/modules/report/util/oConvertUtils.java

@@ -0,0 +1,642 @@
+package com.jeesite.modules.report.util;
+
+import com.jeesite.common.constant.Constants;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.springframework.beans.BeanUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.sql.Date;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ *
+ * @Author  张代浩
+ *
+ */
+@Slf4j
+public class oConvertUtils {
+    public static boolean isEmpty(Object object) {
+        if (object == null) {
+            return (true);
+        }
+        if ("".equals(object)) {
+            return (true);
+        }
+        if (Constants.STRING_NULL.equals(object)) {
+            return (true);
+        }
+        return (false);
+    }
+
+    public static boolean isNotEmpty(Object object) {
+        if (object != null && !"".equals(object) && !object.equals(Constants.STRING_NULL)) {
+            return (true);
+        }
+        return (false);
+    }
+
+    public static String decode(String strIn, String sourceCode, String targetCode) {
+        String temp = code2code(strIn, sourceCode, targetCode);
+        return temp;
+    }
+
+    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
+    public static String StrToUTF(String strIn, String sourceCode, String targetCode) {
+        strIn = "";
+        try {
+            strIn = new String(strIn.getBytes("ISO-8859-1"), "GBK");
+        } catch (UnsupportedEncodingException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        return strIn;
+
+    }
+
+    private static String code2code(String strIn, String sourceCode, String targetCode) {
+        String strOut = null;
+        if (strIn == null || "".equals(strIn.trim())) {
+            return strIn;
+        }
+        try {
+            byte[] b = strIn.getBytes(sourceCode);
+            for (int i = 0; i < b.length; i++) {
+                System.out.print(b[i] + "  ");
+            }
+            strOut = new String(b, targetCode);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+        return strOut;
+    }
+
+    public static int getInt(String s, int defval) {
+        if (s == null || s == "") {
+            return (defval);
+        }
+        try {
+            return (Integer.parseInt(s));
+        } catch (NumberFormatException e) {
+            return (defval);
+        }
+    }
+
+    public static int getInt(String s) {
+        if (s == null || s == "") {
+            return 0;
+        }
+        try {
+            return (Integer.parseInt(s));
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    public static int getInt(String s, Integer df) {
+        if (s == null || s == "") {
+            return df;
+        }
+        try {
+            return (Integer.parseInt(s));
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    public static double getDouble(String s, double defval) {
+        if (s == null || s == "") {
+            return (defval);
+        }
+        try {
+            return (Double.parseDouble(s));
+        } catch (NumberFormatException e) {
+            return (defval);
+        }
+    }
+
+    public static int getInt(Object object, int defval) {
+        if (isEmpty(object)) {
+            return (defval);
+        }
+        try {
+            return (Integer.parseInt(object.toString()));
+        } catch (NumberFormatException e) {
+            return (defval);
+        }
+    }
+
+    public static Integer getInt(Object object) {
+        if (isEmpty(object)) {
+            return null;
+        }
+        try {
+            return (Integer.parseInt(object.toString()));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    public static Integer[] getIntegerArry(String[] object) {
+        int len = object.length;
+        Integer[] result = new Integer[len];
+        try {
+            for (int i = 0; i < len; i++) {
+                result[i] = new Integer(object[i].trim());
+            }
+            return result;
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    public static String getString(String s) {
+        return (getString(s, ""));
+    }
+
+    /**
+     * 转义成Unicode编码
+     * @param
+     * @return
+     */
+	/*public static String escapeJava(Object s) {
+		return StringEscapeUtils.escapeJava(getString(s));
+	}*/
+
+    public static String getString(Object object) {
+        if (isEmpty(object)) {
+            return "";
+        }
+        return (object.toString().trim());
+    }
+
+    public static String getString(int i) {
+        return (String.valueOf(i));
+    }
+
+    public static String getString(float i) {
+        return (String.valueOf(i));
+    }
+
+    public static String getString(String s, String defval) {
+        if (isEmpty(s)) {
+            return (defval);
+        }
+        return (s.trim());
+    }
+
+    public static String getString(Object s, String defval) {
+        if (isEmpty(s)) {
+            return (defval);
+        }
+        return (s.toString().trim());
+    }
+
+    public static long stringToLong(String str) {
+        Long test = new Long(0);
+        try {
+            test = Long.valueOf(str);
+        } catch (Exception e) {
+        }
+        return test.longValue();
+    }
+
+    /**
+     * 获取本机IP
+     */
+    public static String getIp() {
+        String ip = null;
+        try {
+            InetAddress address = InetAddress.getLocalHost();
+            ip = address.getHostAddress();
+
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+        return ip;
+    }
+
+    /**
+     * 判断一个类是否为基本数据类型。
+     *
+     * @param clazz
+     *            要判断的类。
+     * @return true 表示为基本数据类型。
+     */
+    private static boolean isBaseDataType(Class clazz) throws Exception {
+        return (clazz.equals(String.class) || clazz.equals(Integer.class) || clazz.equals(Byte.class) || clazz.equals(Long.class) || clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Character.class) || clazz.equals(Short.class) || clazz.equals(BigDecimal.class) || clazz.equals(BigInteger.class) || clazz.equals(Boolean.class) || clazz.equals(Date.class) || clazz.isPrimitive());
+    }
+
+    /**
+     * @param request
+     *            IP
+     * @return IP Address
+     */
+    public static String getIpAddrByRequest(HttpServletRequest request) {
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || Constants.UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || Constants.UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || Constants.UNKNOWN.equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return ip;
+    }
+
+    /**
+     * @return 本机IP
+     * @throws SocketException
+     */
+    public static String getRealIp() throws SocketException {
+        // 本地IP,如果没有配置外网IP则返回它
+        String localip = null;
+        // 外网IP
+        String netip = null;
+
+        Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
+        InetAddress ip = null;
+        // 是否找到外网IP
+        boolean finded = false;
+        while (netInterfaces.hasMoreElements() && !finded) {
+            NetworkInterface ni = netInterfaces.nextElement();
+            Enumeration<InetAddress> address = ni.getInetAddresses();
+            while (address.hasMoreElements()) {
+                ip = address.nextElement();
+                // 外网IP
+                if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {
+                    netip = ip.getHostAddress();
+                    finded = true;
+                    break;
+                } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {
+                    // 内网IP
+                    localip = ip.getHostAddress();
+                }
+            }
+        }
+
+        if (netip != null && !"".equals(netip)) {
+            return netip;
+        } else {
+            return localip;
+        }
+    }
+
+    /**
+     * java去除字符串中的空格、回车、换行符、制表符
+     *
+     * @param str
+     * @return
+     */
+    public static String replaceBlank(String str) {
+        String dest = "";
+        if (str != null) {
+            String reg = "\\s*|\t|\r|\n";
+            Pattern p = Pattern.compile(reg);
+            Matcher m = p.matcher(str);
+            dest = m.replaceAll("");
+        }
+        return dest;
+
+    }
+
+    /**
+     * 判断元素是否在数组内
+     *
+     * @param substring
+     * @param source
+     * @return
+     */
+    public static boolean isIn(String substring, String[] source) {
+        if (source == null || source.length == 0) {
+            return false;
+        }
+        for (int i = 0; i < source.length; i++) {
+            String aSource = source[i];
+            if (aSource.equals(substring)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 获取Map对象
+     */
+    public static Map<Object, Object> getHashMap() {
+        return new HashMap<>(5);
+    }
+
+    /**
+     * SET转换MAP
+     *
+     * @param setobj
+     * @return
+     */
+    public static Map<Object, Object> setToMap(Set<Object> setobj) {
+        Map<Object, Object> map = getHashMap();
+        for (Iterator iterator = setobj.iterator(); iterator.hasNext();) {
+            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) iterator.next();
+            map.put(entry.getKey().toString(), entry.getValue() == null ? "" : entry.getValue().toString().trim());
+        }
+        return map;
+
+    }
+
+    private static long getIpNum(String ipAddress) {
+        String[] ip = ipAddress.split("\\.");
+        long a = Integer.parseInt(ip[0]);
+        long b = Integer.parseInt(ip[1]);
+        long c = Integer.parseInt(ip[2]);
+        long d = Integer.parseInt(ip[3]);
+
+        long ipNum = a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
+        return ipNum;
+    }
+
+    private static boolean isInner(long userIp, long begin, long end) {
+        return (userIp >= begin) && (userIp <= end);
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。
+     * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+     * 例如:hello_world->helloWorld
+     *
+     * @param name
+     *            转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String camelName(String name) {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty()) {
+            // 没必要转换
+            return "";
+        } else if (!name.contains(Constants.UNDERLINE)) {
+            // 不含下划线,仅将首字母小写
+            //update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+            //update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+            return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
+            //update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels) {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty()) {
+                continue;
+            }
+            // 处理真正的驼峰片段
+            if (result.length() == 0) {
+                // 第一个驼峰片段,全部字母都小写
+                result.append(camel.toLowerCase());
+            } else {
+                // 其他的驼峰片段,首字母大写
+                result.append(camel.substring(0, 1).toUpperCase());
+                result.append(camel.substring(1).toLowerCase());
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。
+     * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+     * 例如:hello_world,test_id->helloWorld,testId
+     *
+     * @param names
+     *            转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String camelNames(String names) {
+        if(names==null||"".equals(names)){
+            return null;
+        }
+        StringBuffer sf = new StringBuffer();
+        String[] fs = names.split(",");
+        for (String field : fs) {
+            field = camelName(field);
+            sf.append(field + ",");
+        }
+        String result = sf.toString();
+        return result.substring(0, result.length() - 1);
+    }
+
+    //update-begin--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
+     * 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
+     * 例如:hello_world->HelloWorld
+     *
+     * @param name
+     *            转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String camelNameCapFirst(String name) {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty()) {
+            // 没必要转换
+            return "";
+        } else if (!name.contains(Constants.UNDERLINE)) {
+            // 不含下划线,仅将首字母小写
+            return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase();
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels) {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty()) {
+                continue;
+            }
+            // 其他的驼峰片段,首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+    //update-end--Author:zhoujf  Date:20180503 for:TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
+
+    /**
+     * 将驼峰命名转化成下划线
+     * @param para
+     * @return
+     */
+    public static String camelToUnderline(String para) {
+        int length = 3;
+        if (para.length() < length) {
+            return para.toLowerCase();
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < para.length(); i++) {
+            //从第三个字符开始 避免命名不规范
+            if (i > 1 & Character.isUpperCase(para.charAt(i))) {
+                sb.append("_");
+            }
+            sb.append(Character.toLowerCase(para.charAt(i)));
+        }
+        return sb.toString().toLowerCase();
+    }
+
+    /**
+     * 随机数
+     * @param place 定义随机数的位数
+     */
+    public static String randomGen(int place) {
+        String base = "qwertyuioplkjhgfdsazxcvbnmQAZWSXEDCRFVTGBYHNUJMIKLOP0123456789";
+        StringBuffer sb = new StringBuffer();
+//		Random rd = new Random();
+        //TODO 中危类-密码中使用伪随机数
+        SecureRandom rd = new SecureRandom();
+        for(int i=0;i<place;i++) {
+            sb.append(base.charAt(rd.nextInt(base.length())));
+        }
+        return sb.toString();
+    }
+
+    public static void main(String[] args) {
+        System.out.println(oConvertUtils.randomGen(8));
+    }
+
+    /**
+     * 获取类的所有属性,包括父类
+     *
+     * @param object
+     * @return
+     */
+    public static Field[] getAllFields(Object object) {
+        Class<?> clazz = object.getClass();
+        List<Field> fieldList = new ArrayList<>();
+        while (clazz != null) {
+            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
+            clazz = clazz.getSuperclass();
+        }
+        Field[] fields = new Field[fieldList.size()];
+        fieldList.toArray(fields);
+        return fields;
+    }
+
+    /**
+     * 将map的key全部转成小写
+     * @param list
+     * @return
+     */
+    public static List<Map<String, Object>> toLowerCasePageList(List<Map<String, Object>> list){
+        List<Map<String, Object>> select = new ArrayList<>();
+        for (Map<String, Object> row : list) {
+            Map<String, Object> resultMap = new HashMap<>(5);
+            Set<String> keySet = row.keySet();
+            for (String key : keySet) {
+                String newKey = key.toLowerCase();
+                resultMap.put(newKey, row.get(key));
+            }
+            select.add(resultMap);
+        }
+        return select;
+    }
+
+    /**
+     * 将entityList转换成modelList
+     * @param fromList
+     * @param tClass
+     * @param <F>
+     * @param <T>
+     * @return
+     */
+    public static<F,T> List<T> entityListToModelList(List<F> fromList, Class<T> tClass){
+        if(fromList == null || fromList.isEmpty()){
+            return null;
+        }
+        List<T> tList = new ArrayList<>();
+        for(F f : fromList){
+            T t = entityToModel(f, tClass);
+            tList.add(t);
+        }
+        return tList;
+    }
+
+    public static<F,T> T entityToModel(F entity, Class<T> modelClass) {
+        log.debug("entityToModel : Entity属性的值赋值到Model");
+        Object model = null;
+        if (entity == null || modelClass ==null) {
+            return null;
+        }
+
+        try {
+            model = modelClass.newInstance();
+        } catch (InstantiationException e) {
+            log.error("entityToModel : 实例化异常", e);
+        } catch (IllegalAccessException e) {
+            log.error("entityToModel : 安全权限异常", e);
+        }
+        BeanUtils.copyProperties(entity, model);
+        return (T)model;
+    }
+
+    /**
+     * 判断 list 是否为空
+     *
+     * @param list
+     * @return true or false
+     * list == null		: true
+     * list.size() == 0	: true
+     */
+    public static boolean listIsEmpty(Collection list) {
+        return (list == null || list.size() == 0);
+    }
+
+    /**
+     * 判断 list 是否不为空
+     *
+     * @param list
+     * @return true or false
+     * list == null		: false
+     * list.size() == 0	: false
+     */
+    public static boolean listIsNotEmpty(Collection list) {
+        return !listIsEmpty(list);
+    }
+
+    /**
+     * 读取静态文本内容
+     * @param url
+     * @return
+     */
+    public static String readStatic(String url) {
+        String json = "";
+        InputStream stream = null;
+        try {
+            //换个写法,解决springboot读取jar包中文件的问题
+            stream = oConvertUtils.class.getClassLoader().getResourceAsStream(url.replace("classpath:", ""));
+            json = IOUtils.toString(stream,"UTF-8");
+        } catch (IOException e) {
+            log.error(e.getMessage(),e);
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    log.error(e.getMessage(),e);
+                }
+            }
+        }
+        return json;
+    }
+}

+ 38 - 74
modules/report/src/main/java/com/jeesite/modules/report/web/WebsiteUserController.java

@@ -1,27 +1,23 @@
 package com.jeesite.modules.report.web;
 
-import java.util.List;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
+import com.jeesite.common.entity.Page;
+import com.jeesite.common.web.BaseController;
+import com.jeesite.modules.report.entity.WebsiteUser;
+import com.jeesite.modules.report.service.WebsiteUserService;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
-import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import com.jeesite.common.config.Global;
-import com.jeesite.common.entity.Page;
-import com.jeesite.common.web.BaseController;
-import com.jeesite.modules.report.entity.WebsiteUser;
-import com.jeesite.modules.report.service.WebsiteUserService;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 /**
  * 网站用户Controller
+ *
  * @author gg
  * @version 2024-12-19
  */
@@ -29,69 +25,37 @@ import com.jeesite.modules.report.service.WebsiteUserService;
 @RequestMapping(value = "${adminPath}/report/websiteUser")
 public class WebsiteUserController extends BaseController {
 
-	@Autowired
-	private WebsiteUserService websiteUserService;
-	
-	/**
-	 * 获取数据
-	 */
-	@ModelAttribute
-	public WebsiteUser get(String id, boolean isNewRecord) {
-		return websiteUserService.get(id, isNewRecord);
-	}
-	
-	/**
-	 * 查询列表
-	 */
-	@RequiresPermissions("report:websiteUser:view")
-	@RequestMapping(value = {"list", ""})
-	public String list(WebsiteUser websiteUser, Model model) {
-		model.addAttribute("websiteUser", websiteUser);
-		return "modules/report/websiteUserList";
-	}
-	
-	/**
-	 * 查询列表数据
-	 */
-	@RequiresPermissions("report:websiteUser:view")
-	@RequestMapping(value = "listData")
-	@ResponseBody
-	public Page<WebsiteUser> listData(WebsiteUser websiteUser, HttpServletRequest request, HttpServletResponse response) {
-		websiteUser.setPage(new Page<>(request, response));
-		Page<WebsiteUser> page = websiteUserService.findPage(websiteUser);
-		return page;
-	}
+    @Autowired
+    private WebsiteUserService websiteUserService;
+
+    /**
+     * 获取数据
+     */
+    @ModelAttribute
+    public WebsiteUser get(String id, boolean isNewRecord) {
+        return websiteUserService.get(id, isNewRecord);
+    }
+
+    /**
+     * 查询列表数据
+     */
+//	@RequiresPermissions("report:websiteUser:view")
+    @RequestMapping(value = "listData")
+    @ResponseBody
+    public Page<WebsiteUser> listData(WebsiteUser websiteUser, HttpServletRequest request, HttpServletResponse response) {
+        websiteUser.setPage(new Page<>(request, response));
+        Page<WebsiteUser> page = websiteUserService.findPage(websiteUser);
+        return page;
+    }
 
-	/**
-	 * 查看编辑表单
-	 */
-	@RequiresPermissions("report:websiteUser:view")
-	@RequestMapping(value = "form")
-	public String form(WebsiteUser websiteUser, Model model) {
-		model.addAttribute("websiteUser", websiteUser);
-		return "modules/report/websiteUserForm";
-	}
+    /**
+     * 查看编辑表单
+     */
+    @RequiresPermissions("report:websiteUser:view")
+    @RequestMapping(value = "form")
+    public String form(WebsiteUser websiteUser, Model model) {
+        model.addAttribute("websiteUser", websiteUser);
+        return "modules/report/websiteUserForm";
+    }
 
-	/**
-	 * 保存数据
-	 */
-	@RequiresPermissions("report:websiteUser:edit")
-	@PostMapping(value = "save")
-	@ResponseBody
-	public String save(@Validated WebsiteUser websiteUser) {
-		websiteUserService.save(websiteUser);
-		return renderResult(Global.TRUE, text("保存网站用户成功!"));
-	}
-	
-	/**
-	 * 删除数据
-	 */
-	@RequiresPermissions("report:websiteUser:edit")
-	@RequestMapping(value = "delete")
-	@ResponseBody
-	public String delete(WebsiteUser websiteUser) {
-		websiteUserService.delete(websiteUser);
-		return renderResult(Global.TRUE, text("删除网站用户成功!"));
-	}
-	
 }

+ 1 - 0
web/jeesite-web.iml

@@ -204,5 +204,6 @@
     <orderEntry type="module" module-name="jeesite-module-report" />
     <orderEntry type="module" module-name="jeesite-module-basedata" />
     <orderEntry type="library" name="Maven: org.dom4j:dom4j:2.1.3" level="project" />
+    <orderEntry type="library" name="Maven: com.auth0:java-jwt:3.11.0" level="project" />
   </component>
 </module>

+ 3 - 3
web/src/main/resources/config/application.yml

@@ -263,11 +263,11 @@ spring:
 
   # Redis 连接参数 (RedisProperties)
   redis:
-    host: 127.0.0.1
+    host: 192.168.0.119
     port: 6379
     ssl: false
-    database: 0
-    password:
+    database: 15
+    password: songlanyun
     timeout: 20000
     lettuce:
       pool: