Compare commits

...

55 Commits

Author SHA1 Message Date
thinkgem
3e6044d902 v4.0.6 2018-08-14 21:14:21 +08:00
thinkgem
15dcb1b27d update 2018-08-13 21:35:43 +08:00
thinkgem
849b3a7ec0 将web项目的config文件分散到jar包里,让web项目更简洁。 2018-08-12 19:55:32 +08:00
thinkgem
225a6a94ad 新增服务器监控功能,查看CPU,内存,JVM,磁盘信息等 2018-08-12 18:23:48 +08:00
thinkgem
8c52dd5db1 通用CrudDao基类新增物理删除方法,phyDelete、phyDeleteByEntity。 2018-08-11 23:09:17 +08:00
thinkgem
300bbd5f8f 优化XSS过滤方法,解决一些可能会发生的问题。 2018-08-11 12:59:03 +08:00
thinkgem
099ce0758b Excel导入导出的缓存清理优化,增加close方法,杜绝可能会造成内存泄露的问题。 2018-08-11 12:43:57 +08:00
thinkgem
f91bb55f38 支持指定获取客户端IP的Header名称,防止IP伪造。 2018-08-09 23:26:17 +08:00
thinkgem
0e67815b8d 修正用户列表选择的selectData变量可能造成XSS漏洞 2018-08-09 22:45:19 +08:00
thinkgem
19ba6daea7 修正登录页记住账号在DES加密的情况下,会有XSS漏洞。 2018-08-09 21:04:26 +08:00
thinkgem
45a09933b0 新增参数:是否在登录后生成新的Session(默认false)详见 jeesite.yml 2018-08-08 21:40:24 +08:00
thinkgem
fc88e47a4b update 2018-08-06 22:04:49 +08:00
thinkgem
dc088a4764 增加MyBatis的Map参数传递和返回实例,支持分页。 2018-08-05 22:12:30 +08:00
thinkgem
6895d57cfa update readme.md 2018-08-04 15:51:26 +08:00
thinkgem
1b09cf9969 优化StringUtils.camelCase驼峰命名法转换,不允许第二个字符是大写,因为这样会造成bean引用失败 2018-08-04 15:42:49 +08:00
thinkgem
086ca1d882 修正属性配置文件表达式为空时可能报错问题。 2018-08-02 21:17:31 +08:00
thinkgem
d336c0fcf7 update terms.md 2018-08-02 21:13:10 +08:00
thinkgem
9736100fdc 新增支持无页签模式和表单弹窗模式(专业版)。 2018-07-29 21:45:02 +08:00
thinkgem
38acf1ae16 Global增加getConfigToBoolean、getConfigToInteger、getPropertyToBoolean、getPropertyToInteger方法 2018-07-29 21:41:59 +08:00
thinkgem
7ffa86df6e 参数配置的参数值config_value允许空值 2018-07-29 21:34:17 +08:00
thinkgem
2a8a7edb83 优化用户树选择查询。 2018-07-28 00:10:28 +08:00
thinkgem
4cf49b7446 Revert "Revert "add update script""
This reverts commit 9db7917975.
2018-07-22 23:49:08 +08:00
thinkgem
9db7917975 Revert "add update script"
This reverts commit 095ecddc8d45c7e63c66cf086b8095ffc18cf50d.
2018-07-22 23:47:05 +08:00
thinkgem
5dfa01c421 优化shiro.successUrl登录成功后跳转页面参数,支持ajax登录后跳转,sso下登录跳转 2018-07-22 23:47:05 +08:00
thinkgem
4f8e2ccb4e 增加一些连接池常用参数,详见 jeesite-core.yml 2018-07-22 23:47:04 +08:00
thinkgem
b643b97603 新增缓存监控功能,查看缓存内存,清理缓存等 2018-07-22 23:47:03 +08:00
thinkgem
e651f23210 update pom version 2018-07-22 14:04:50 +08:00
thinkgem
8ecd600155 update terms.md 2018-07-22 14:04:37 +08:00
thinkgem
45fabc8ca0 降低ReflectUtils的错误级别,如果null不抛错,打印日志警告即可。 2018-07-22 12:16:31 +08:00
thinkgem
89fdfc2070 Merge branch 'master' of https://gitee.com/thinkgem/jeesite4
# Conflicts:
#	web/bin/init-data.bat
#	web/bin/init-data.sh
2018-07-16 23:29:08 +08:00
thinkgem
1880b0bc4c update README.md 2018-07-16 22:47:19 +08:00
thinkgem
949e747435 update README.md 2018-07-16 22:35:10 +08:00
thinkgem
f2fb1af04f 新增员工用户列表选择示例代码,详见帮助文档form:listselect 2018-07-15 23:37:53 +08:00
thinkgem
ddcf75b856 微不足道的一些优化。 2018-07-15 16:05:36 +08:00
thinkgem
638c57b9c0 PinyinUtils优化 2018-07-14 22:22:24 +08:00
thinkgem
7da392e574 bin脚本优化,增加一些脚本修改的提示帮助。 2018-07-13 22:15:00 +08:00
thinkgem
c21bf3ebfd update terms.md 2018-07-13 22:13:49 +08:00
thinkgem
b1380add20 优化错误页面的错误信息展示 2018-07-13 22:06:20 +08:00
thinkgem
d001756f6e 支持 spring.profiles.active=dev
的jvm参数或yml配置,活动环境名称参数,环境配置如:application-dev.yml
2018-07-12 23:42:45 +08:00
thinkgem
941be6a52e job支持指定数据源名称,数据源监控显示数据源名称 2018-07-12 23:37:47 +08:00
thinkgem
378bec08f2 为了防止初始化数据表单元测试误运行,增加 -Djeesite.initdata=true 参数作为校验。 2018-07-11 23:10:08 +08:00
thinkgem
47e2f219f5 拆离出CAS权限授权的Realm,支持开发者自定义Realm(个人版) 2018-07-11 22:40:59 +08:00
thinkgem
81793205c1 新增找回密码功能,支持通过手机号、邮箱、保密问题找回。 2018-07-08 22:45:10 +08:00
thinkgem
e55fb4274d update README.md 2018-07-08 10:06:41 +08:00
thinkgem
0af6fedef8 @Table@Column支持isUpdateForce=true强制更新,不再判断值非空情况下才加入更新。 2018-07-04 21:26:54 +08:00
ThinkGem
a8cc129464 更新 terms.md 2018-07-03 10:12:37 +08:00
thinkgem
26ee250d4a ResourceUtils工具类优化 2018-07-02 23:23:15 +08:00
thinkgem
f44d13ddec 支持war包不解压的情况下运行。 2018-07-02 23:00:37 +08:00
thinkgem
606f773ebc 修正500页面,没有ex.cause的情况报空值针问题。 2018-07-02 21:25:15 +08:00
thinkgem
300dfdcd78 update init-data.bat/sh 2018-06-28 21:06:27 +08:00
thinkgem
5a9c225a41 common.css/js webapp to resources 2018-06-28 20:19:45 +08:00
ThinkGem
a1577453c3 更新 init-data.sh 2018-06-28 18:19:57 +08:00
ThinkGem
2288884964 更新 init-data.bat 2018-06-28 18:19:18 +08:00
thinkgem
686a138234 update pom version 2018-06-25 23:10:17 +08:00
thinkgem
1058c49b7f 修正用户身份为二级管理员的时候数据权限设置不正确问题 2018-06-21 20:10:06 +08:00
94 changed files with 1873 additions and 1000 deletions

View File

@@ -2,66 +2,65 @@
JeeSite 是一个 Java EE 企业级快速开发平台基于经典技术组合Spring Boot、Spring MVC、Apache Shiro、MyBatis、Beetl、Bootstrap、AdminLTE在线代码生成功能包括核心模块如组织机构、角色用户、菜单及按钮授权、数据权限、系统参数、内容管理、工作流等。采用松耦合设计界面无刷新一键换肤众多账号安全设置密码策略在线定时任务配置支持集群支持SAAS支持多数据源。
JeeSite 快速开发平台的主要目的是能够让初级的研发人员快速的开发出复杂的业务功能,让开发者注重专注业务,其余有平台来封装技术细节,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。
JeeSite 快速开发平台的主要目的是能够让初级的研发人员快速的开发出复杂的业务功能(经典架构会的人多),让开发者注重专注业务,其余有平台来封装技术细节,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量。
JeeSite 自开源以来已被广大爱好者用到了企业、政府、医疗、金融、互联网等各个领域中JeeSite 依架构简单精良、易于扩展、大众思维的设计模式,深入开发者的内心,并得到一致好评,于[2016](http://www.oschina.net/project/top_cn_2016?sort=1)和[2017](http://www.oschina.net/project/top_cn_2017?sort=1)连续两年获得开源中国《最受欢迎中国开源软件》奖杯,期间也帮助了不少刚毕业的大学生作为入门教材,快速的去实践。
现在 JeeSite 4.0 来了,4.0的升级,作者结合了多年总结和经验,以及各方面的应用案例,对架构完成了一次全部重构,也纳入很多新的思想。不管是从开发者模式、底层架构、逻辑处理还是到用户界面,用户交互体验上都有很大的进步,在不忘学习成本、提高开发效率的情况下,安全方面也做和很多工作,包括:身份认证、密码策略、安全审计、日志收集。
4.0的升级,作者结合了多年总结和经验,以及各方面的应用案例,对架构完成了一次全部重构,也纳入很多新的思想。不管是从开发者模式、底层架构、逻辑处理还是到用户界面,用户交互体验上都有很大的进步,在不忘学习成本、提高开发效率的情况下,安全方面也做和很多工作,包括:身份认证、密码策略、安全审计、日志收集。
# 快速了解 JeeSite 4.0
**我们的优势:** 架构清晰、技术先进、入门简单、易于维护、易于扩展、安全稳定。
* **JeeSite 4.0 新特性、技术选型**
### 4.0 新特性
<https://my.oschina.net/thinkgem/blog/913777>
* <http://jeesite4.mydoc.io/?t=281645>
* **JeeSite 4.0 简化MyBatis持久层开发**
## 技术选型
<https://my.oschina.net/thinkgem/blog/1503611>
* 主框架Spring Boot 1.5、Spring Framework 4.3、Apache Shiro 1.4
* 持久层Apache MyBatis 3.4、Hibernate Validation 5.3、Alibaba Druid 1.1
* 视图层Spring MVC 4.3、Beetl 2.7 替换JSP、Bootstrap 3.3、AdminLTE 2.4
* 前端组件jQuery 1.12、jqGrid 4.7、layer 3.0、zTree 3.5、jquery-validation
* 工具组件Apache Commons、Logback 1.1、Jackson 2.8、POI 3.14、Quartz 2.2
* 乐云短信网关SmsUtils.java <http://www.lehuo520.cn>
* 技术选型详情:<http://jeesite4.mydoc.io/?t=273599>
* **JeeSite 4.0 简化业务逻辑层开发**
## 内置功能菜单
<https://my.oschina.net/thinkgem/blog/1538766>
* <http://jeesite4.mydoc.io/?t=270187>
* **JeeSite 4.0 MVC层及前端组件介绍**
## 开发手册
<https://my.oschina.net/thinkgem/blog/1561129>
* **JeeSite 4.0 内置功能模块介绍**
<https://my.oschina.net/thinkgem/blog/1609852>
<https://my.oschina.net/thinkgem/blog/1630671>
* **JeeSite 4.0 开发文档**
<http://jeesite4.mydoc.io>
* 持久层开发手册 (Dao)<http://jeesite4.mydoc.io/?t=267351>
* 业务层开发手册 (Service)<http://jeesite4.mydoc.io/?t=267352>
* 视图层开发手册 (WebView)<http://jeesite4.mydoc.io/?t=267353>
* 权限管理应用基础 (Shiro)<http://jeesite4.mydoc.io/?t=298473>
* 常用JS类库API (jeesite.js)<http://jeesite4.mydoc.io/?t=301473>
* 数据表格API (DataGrid.js)<http://jeesite4.mydoc.io/?t=301488>
* 修改 (默认) 视图,新增主题:<http://jeesite4.mydoc.io/?t=267355>
* 手机 API 接口调用、前后分离:<http://jeesite4.mydoc.io/?t=270527>
# 快速体验
### 在线演示
* 地址:<http://demo.jeesite.net/>
* 账号system
* 密码admin
1. 地址:<http://demo.jeesite.com/>
2. 账号system
3. 密码admin
### 本地运行
1. 环境准备:`JDK 1.8``Maven 3.3``MySQL 5.7`
2. 下载源码:<https://gitee.com/thinkgem/jeesite4/attach_files>
3. 打开文件 /web`/src/main/resources/config/jeesite.yml` 配置JDBC连接
4. 执行脚本 /web`/bin/init-data.bat` 初始化数据库
5. 执行脚本 /web`/bin/run-tomcat.bat` 启动服务即可
6. 浏览器访问 <http://127.0.0.1:8980/js/> 账号 system 密码 admin
3. 打开文件/web`/src/main/resources/config/jeesite.yml` 配置JDBC连接
4. 执行脚本/web`/bin/init-data.bat` 初始化数据库
5. 执行脚本/web`/bin/run-tomcat.bat` 启动服务即可
6. 浏览器访问<http://127.0.0.1:8980/js/> 账号 system 密码 admin
7. 常见问题:<http://jeesite4.mydoc.io/?t=284210>
### 开发环境
* **开发环境部署运行调试**
<http://jeesite4.mydoc.io/?t=267354>
* **常见问题汇总**
<http://jeesite4.mydoc.io/?t=284210>
1. 部署运行:<http://jeesite4.mydoc.io/?t=267354>
2. 常见问题:<http://jeesite4.mydoc.io/?t=284210>
# 今后如何升级?
@@ -91,11 +90,10 @@ JeeSite的小版本4.0.x升级是非常便捷的你只需要将 pom.xml
# 技术交流方式
* QQ 群号: `127515876(已满)` `209330483(已满)` `223507718(已满)` `709534275` `730390092`
* 入群须知目前为付费群刚入群会有5分钟禁言腾讯预设置的无法解除稍等片刻即可正常发言由于群容量有限为了维持运营千人QQ群的所需支付的QQ年费会员费用故开启付费入群模式申请者只需支付少量金额即可加入这样也可以保证只有真实交流需求的人进入避免闲杂做广告人员的乱入新手提问前请先阅读此[【文章](http://www.dianbo.org/9238/stone/tiwendezhihui.htm)
* Gitee<https://gitee.com/thinkgem/jeesite4>
* 问题反馈:<https://gitee.com/thinkgem/jeesite4/issues>  [【新手必读](http://www.dianbo.org/9238/stone/tiwendezhihui.htm)
* 码云Gitee<https://gitee.com/thinkgem/jeesite4>
* GitHub<https://github.com/thinkgem/jeesite4>
* 问题反馈<https://gitee.com/thinkgem/jeesite4/issues>
* 作者博客:<https://my.oschina.net/thinkgem/blog>
* 作者博客<https://my.oschina.net/thinkgem>
* 官方网站:<http://jeesite.com>
* 微信公众号:

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>4.0.4-SNAPSHOT</version>
<version>4.0.6-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>

View File

@@ -8,6 +8,8 @@ package com.jeesite.common.codec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.codec.DecoderException;
@@ -17,6 +19,7 @@ import org.apache.commons.lang3.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.lang.ExceptionUtils;
import com.jeesite.common.lang.StringUtils;
@@ -167,7 +170,9 @@ public class EncodeUtils {
* URL 解码, Encode默认为UTF-8.
*/
public static String decodeUrl(String part, String encoding) {
if (part == null){
return null;
}
try {
return URLDecoder.decode(part, encoding);
} catch (UnsupportedEncodingException e) {
@@ -181,30 +186,29 @@ public class EncodeUtils {
public static String decodeUrl2(String part) {
return decodeUrl(decodeUrl(part));
}
// 预编译XSS过滤正则表达式
private static Pattern p1 = Pattern.compile("<\\s*(script|link|style|iframe)\\s([\\s\\S]+?)<\\/\\s*\\1\\s*>", Pattern.CASE_INSENSITIVE);
private static Pattern p2 = Pattern.compile("\\s*on[a-z]+\\s*=\\s*(\"[^\"]+\"|'[^']+'|[^\\s]+)\\s*(?=>)", Pattern.CASE_INSENSITIVE);
private static Pattern p3 = Pattern.compile("\\s*(href|src)\\s*=\\s*(\"\\s*(javascript|vbscript):[^\"]+\"|'\\s*(javascript|vbscript):[^']+'|(javascript|vbscript):[^\\s]+)\\s*(?=>)", Pattern.CASE_INSENSITIVE);
private static Pattern p4 = Pattern.compile("epression\\((.|\\n)*\\);?", Pattern.CASE_INSENSITIVE);
private static List<Pattern> xssPatterns = ListUtils.newArrayList(
Pattern.compile("(<\\s*(script|link|style|iframe)([\\s\\S]*?)(>|<\\/\\s*\\1\\s*>))|(</\\s*(script|link|style|iframe)\\s*>)", Pattern.CASE_INSENSITIVE),
Pattern.compile("\\s*(href|src)\\s*=\\s*(\"\\s*(javascript|vbscript):[^\"]+\"|'\\s*(javascript|vbscript):[^']+'|(javascript|vbscript):[^\\s]+)\\s*(?=>)", Pattern.CASE_INSENSITIVE),
Pattern.compile("\\s*on[a-z]+\\s*=\\s*(\"[^\"]+\"|'[^']+'|[^\\s]+)\\s*(?=>)", Pattern.CASE_INSENSITIVE),
Pattern.compile("(eval\\((.|\\n)*\\)|xpression\\((.|\\n)*\\))", Pattern.CASE_INSENSITIVE)
);
/**
* XSS 非法字符过滤
* 内容以<!--HTML-->开头的用以下规则保留标签去掉js脚本
* 1、<\s*(script|link|style|iframe)\s([\s\S]+?)<\/\s*\1\s*>
* 2、\s*on[a-z]+\s*=\s*("[^"]+"|'[^']+'|[^\s]+)\s*(?=>)
* 3、\s*(href|src)\s*=\s*("\s*(javascript|vbscript):[^"]+"|'\s*(javascript|vbscript):[^']+'|(javascript|vbscript):[^\s]+)\s*(?=>)
* 4、epression\((.|\n)*\);?
* 其它情况下进行HTML4编码
* XSS 非法字符过滤,内容以<!--HTML-->开头的用以下规则(保留标签)
* @author ThinkGem
*/
public static String xssFilter(String text) {
String oriValue = StringUtils.trim(text);
if (text != null){
String oriValue = StringUtils.trim(text), value = oriValue;
value = p1.matcher(value).replaceAll("");
value = p2.matcher(value).replaceAll("");
value = p3.matcher(value).replaceAll("");
value = p4.matcher(value).replaceAll("");
String value = oriValue;
for (Pattern pattern : xssPatterns) {
Matcher matcher = pattern.matcher(value);
if (matcher.find()) {
value = matcher.replaceAll(StringUtils.EMPTY);
}
}
// 如果开始不是HTMLXMLJOSN格式则再进行HTML的 "、<、> 转码。
if (!StringUtils.startsWithIgnoreCase(value, "<!--HTML-->") // HTML
&& !StringUtils.startsWithIgnoreCase(value, "<?xml ") // XML
@@ -215,7 +219,7 @@ public class EncodeUtils {
value = value.replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
}
if (logger.isInfoEnabled() && !value.equals(oriValue)){
logger.info("xssFilter: {} to {}", text, value);
logger.info("xssFilter: {} <=<=<= {}", value, text);
}
return value;
}
@@ -223,7 +227,7 @@ public class EncodeUtils {
}
// 预编译SQL过滤正则表达式
private static Pattern p5 = Pattern.compile("(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)", Pattern.CASE_INSENSITIVE);
private static Pattern sqlPattern = Pattern.compile("(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)", Pattern.CASE_INSENSITIVE);
/**
* SQL过滤防止注入传入参数输入有select相关代码替换空。
@@ -231,13 +235,48 @@ public class EncodeUtils {
*/
public static String sqlFilter(String text){
if (text != null){
String value = p5.matcher(text).replaceAll("");
String value = text;
Matcher matcher = sqlPattern.matcher(text);
if (matcher.find()) {
value = matcher.replaceAll(StringUtils.EMPTY);
}
if (logger.isWarnEnabled() && !value.equals(text)){
logger.warn("sqlFilter: {} to {}", text, value);
logger.info("sqlFilter: {} <=<=<= {}", value, text);
return StringUtils.EMPTY;
}
return value;
}
return null;
}
// public static void main(String[] args) {
// xssFilter("你好,<script>alert(document.cookie)</script>我还在。");
// xssFilter("你好,<strong>加粗文字</strong>我还在。");
// xssFilter("<!--HTML-->你好,\"><strong>加粗文字</strong>我还在。");
// xssFilter("<!--HTML-->你好,<iframe src=\"abcdef\"></iframe><strong>加粗文字</strong>我还在。");
// xssFilter("<!--HTML-->你好,<iframe src=\"abcdef\"/><strong>加粗文字</strong>我还在。");
// xssFilter("<!--HTML-->你好,<iframe src=\"abcdef\"><strong>加粗文字</strong>我还在。");
// xssFilter("<!--HTML-->你好,<script type=\"text/javascript\">alert(document.cookie)</script>我还在。");
// xssFilter("<!--HTML-->你好,<script\n type=\"text/javascript\">\nalert(document.cookie)\n</script>我还在。");
// xssFilter("<!--HTML-->你好,<script src='' onerror='alert(document.cookie)'></script>我还在。");
// xssFilter("<!--HTML-->你好,<script type=text/javascript>alert()我还在。");
// xssFilter("<!--HTML-->你好,<script>alert(document.cookie)</script>我还在。");
// xssFilter("<!--HTML-->你好,<script>window.location='url'我还在。");
// xssFilter("<!--HTML-->你好,</script></iframe>我还在。");
// xssFilter("<!--HTML-->你好eval(abc)我还在。");
// xssFilter("<!--HTML-->你好xpression(abc)我还在。");
// xssFilter("<!--HTML-->你好,<img src='abc.jpg' onerror='location='';alert(document.cookie);'></img>我还在。");
// xssFilter("<!--HTML-->你好,<img src='abc.jpg' onerror='alert(document.cookie);'/>我还在。");
// xssFilter("<!--HTML-->你好,<img src='abc.jpg' onerror='alert(document.cookie);'>我还在。");
// xssFilter("<!--HTML-->你好,<a onload='alert(\"abc\")'>hello</a>我还在。");
// xssFilter("<!--HTML-->你好,<a href=\"/abc\">hello</a>我还在。");
// xssFilter("<!--HTML-->你好,<a href='/abc'>hello</a>我还在。");
// xssFilter("<!--HTML-->你好,<a href='vbscript:alert(\"abc\");'>hello</a>我还在。");
// xssFilter("<!--HTML-->你好,<a href='javascript:alert(\"abc\");'>hello</a>我还在。");
// xssFilter("<!--HTML-->你好,?abc=def&hello=123&world={\"a\":1}我还在。");
// sqlFilter("你好select * from xxx where abc=def and 1=1我还在。");
// sqlFilter("你好insert into xxx values(1,2,3,4,5)我还在。");
// sqlFilter("你好delete from xxx我还在。");
// }
}

View File

@@ -948,6 +948,10 @@ public class FileUtils extends org.apache.commons.io.FileUtils {
} catch (IOException e) {
e.printStackTrace();
}
// 取不到,取当前工作路径
if (StringUtils.isBlank(projectPath)){
projectPath = System.getProperty("user.dir");
}
return projectPath;
}
@@ -982,6 +986,10 @@ public class FileUtils extends org.apache.commons.io.FileUtils {
} catch (IOException e) {
e.printStackTrace();
}
// 取不到,取当前工作路径
if (StringUtils.isBlank(webappPath)){
webappPath = System.getProperty("user.dir");
}
return webappPath;
}

View File

@@ -34,9 +34,7 @@ public class PropertiesUtils {
// 默认加载的文件可通过继承覆盖若有相同Key优先加载后面的
public static final String[] DEFAULT_CONFIG_FILE = new String[]{
"classpath:config/jeesite.yml",
"classpath:config/application.yml",
"classpath:application.yml"};
"classpath:config/application.yml", "classpath:application.yml"};
private static Logger logger = PropertiesUtils.initLogger();
@@ -51,26 +49,53 @@ public class PropertiesUtils {
releadInstance();
}
public static void releadInstance(){
Set<String> configFiles = SetUtils.newLinkedHashSet();
// 获取平台及模块相关的配置文件
Set<String> configSet = SetUtils.newLinkedHashSet();
Resource[] resources = ResourceUtils.getResources("classpath*:/config/jeesite-*.*");
for(Resource resource : resources){
configFiles.add("classpath:/config/"+resource.getFilename());
configSet.add("classpath:config/"+resource.getFilename());
}
configSet.add("classpath:config/jeesite.yml");
// 获取全局设置默认的配置文件(以下是支持环境配置的属性文件)
Set<String> set = SetUtils.newLinkedHashSet();
for (String configFile : DEFAULT_CONFIG_FILE){
configFiles.add(configFile);
set.add(configFile);
}
String customConfig = System.getProperty("spring.config.location");
if (StringUtils.isNotBlank(customConfig)){
if (!customConfig.contains("$")){
customConfig = org.springframework.util.StringUtils.cleanPath(customConfig);
if (!ResourceUtils.isUrl(customConfig)){
customConfig = ResourceUtils.FILE_URL_PREFIX + customConfig;
// 获取 spring.config.location 外部自定义的配置文件
String customConfigs = System.getProperty("spring.config.location");
if (StringUtils.isNotBlank(customConfigs)){
for (String customConfig : StringUtils.split(customConfigs, ",")){
if (!customConfig.contains("$")){
customConfig = org.springframework.util.StringUtils.cleanPath(customConfig);
if (!ResourceUtils.isUrl(customConfig)){
customConfig = ResourceUtils.FILE_URL_PREFIX + customConfig;
}
}
set.add(customConfig);
}
}
// 获取 spring.profiles.active 活动环境名称的配置文件
String[] configFiles = set.toArray(new String[set.size()]);
String profiles = System.getProperty("spring.profiles.active");
if (StringUtils.isBlank(profiles)){
PropertiesUtils propsTemp = new PropertiesUtils(configFiles);
profiles = propsTemp.getProperty("spring.profiles.active");
}
for (String location : configFiles){
configSet.add(location);
if (StringUtils.isNotBlank(profiles) && !StringUtils.equals(profiles, "default")){
if (location.endsWith(".properties")){
configSet.add(StringUtils.substringBeforeLast(location, ".properties")
+ "-" + profiles + ".properties");
}else if (location.endsWith(".yml")){
configSet.add(StringUtils.substringBeforeLast(location, ".yml")
+ "-" + profiles + ".yml");
}
}
configFiles.add(customConfig);
}
logger.debug("Loading jeesite config: {}", configFiles);
INSTANCE = new PropertiesUtils(configFiles.toArray(new String[configFiles.size()]));
configFiles = configSet.toArray(new String[configSet.size()]);
logger.debug("Loading jeesite config: {}", (Object)configFiles);
INSTANCE = new PropertiesUtils(configFiles);
}
}
@@ -82,8 +107,7 @@ public class PropertiesUtils {
try {
Resource resource = ResourceUtils.getResource(location);
if (resource.exists()){
String ext = FileUtils.getFileExtension(location);
if ("properties".equals(ext)){
if (location.endsWith(".properties")){
InputStreamReader is = null;
try {
is = new InputStreamReader(resource.getInputStream(), "UTF-8");
@@ -94,7 +118,7 @@ public class PropertiesUtils {
IOUtils.closeQuietly(is);
}
}
else if ("yml".equals(ext)){
else if (location.endsWith(".yml")){
YamlPropertiesFactoryBean bean = new YamlPropertiesFactoryBean();
bean.setResources(resource);
for (Map.Entry<Object,Object> entry : bean.getObject().entrySet()){
@@ -106,6 +130,8 @@ public class PropertiesUtils {
} catch (Exception e) {
logger.error("Load " + location + " failure. ", e);
}
// 存储当前加载的配置文件路径和名称
properties.setProperty("configFiles", StringUtils.join(configFiles, ","));
}
}
@@ -144,7 +170,7 @@ public class PropertiesUtils {
while(m.find()) {
String g = m.group();
String keyChild = g.replaceAll("\\$\\{", "").replaceAll("\\}", "");
value = value.replace(g, getProperty(keyChild));
value = StringUtils.replace(value, g, getProperty(keyChild));
}
return value;
}else{

View File

@@ -10,6 +10,7 @@ import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import com.jeesite.common.lang.ExceptionUtils;
@@ -20,7 +21,12 @@ import com.jeesite.common.lang.ExceptionUtils;
*/
public class ResourceUtils extends org.springframework.util.ResourceUtils {
private static ResourceLoader resourceLoader = new DefaultResourceLoader();
private static ResourceLoader resourceLoader;
private static ResourcePatternResolver resourceResolver;
static{
resourceLoader = new DefaultResourceLoader();
resourceResolver = new PathMatchingResourcePatternResolver(resourceLoader);
}
/**
* 获取资源加载器可读取jar内的文件
@@ -79,11 +85,7 @@ public class ResourceUtils extends org.springframework.util.ResourceUtils {
*/
public static Resource[] getResources(String locationPattern){
try {
Resource[] resources = new PathMatchingResourcePatternResolver()
.getResources(locationPattern);
// System.out.println("===========\n===========");
// System.out.println(locationPattern + " : " + resources.length);
// System.out.println("===========\n===========");
Resource[] resources = resourceResolver.getResources(locationPattern);
return resources;
} catch (IOException e) {
throw ExceptionUtils.unchecked(e);

View File

@@ -17,6 +17,10 @@ public class ByteUtils {
* @return
*/
public static String formatByteSize(long byteSize) {
if (byteSize <= -1){
return String.valueOf(byteSize);
}
double size = 1.0 * byteSize;

View File

@@ -14,13 +14,14 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.nustaq.serialization.FSTConfiguration;
import org.springframework.beans.BeanUtils;
import org.springframework.core.NamedThreadLocal;
import com.jeesite.common.io.IOUtils;
/**
* 对象操作工具类, 继承org.apache.commons.lang3.ObjectUtils类
* @author ThinkGem
* @version 2014-6-29
* @version 2018-08-11
*/
public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
@@ -212,73 +213,14 @@ public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
}
return object;
}
// // Kryo不是线程安全的所以要建立一个线程变量每一个线程实例化一次
// public static final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {
// @Override
// protected Kryo initialValue() {
// Kryo kryo = new Kryo();
// // 设置false关闭注册行为 Kryo支持对注册行为如kryo.register(SomeClazz.class);
// // 这会赋予该Class一个从0开始的编号但Kryo使用注册行为最大的问题在于
// // 其不保证同一个Class每一次注册的号码想用这与注册的顺序有关也就意味着在不同的机器、
// // 同一个机器重启前后都有可能拥有不同的编号,这会导致序列化产生问题,所以在分布式项目中,一般关闭注册行为。
// kryo.setRegistrationRequired(false);
// // 支持循环引用
// kryo.setReferences(true);
// return kryo;
// };
// };
//
// /**
// * Kryo序列化对象
// * @param object
// * @return
// */
// public static byte[] serializeKryo(Object object) {
// byte[] bytes = null;
// Output output = null;
// try {
// if (object != null) {
// output = new Output(1024, -1);
// kryos.get().writeClassAndObject(output, object);
// bytes = output.toBytes();
// }
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// if (output != null) {
// output.close();
// }
// }
// return bytes;
// }
//
// /**
// * Kryo反序列化对象
// * @param bytes
// * @return
// */
// public static Object unserializeKryo(byte[] bytes) {
// Object object = null;
// Input input = null;
// try {
// if (bytes != null && bytes.length > 0) {
// input = new Input(bytes, 0, bytes.length);
// object = kryos.get().readClassAndObject(input);
// }
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// if (input != null) {
// input.close();
// }
// }
// return object;
// }
// FST序列化配置对象
private static FSTConfiguration fst = FSTConfiguration.createDefaultConfiguration();
private static ThreadLocal<FSTConfiguration> fst = new NamedThreadLocal<FSTConfiguration>("FSTConfiguration") {
public FSTConfiguration initialValue() {
return FSTConfiguration.createDefaultConfiguration();
}
};
/**
* FST 序列化对象
* @param object
@@ -289,7 +231,7 @@ public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
return null;
}
long beginTime = System.currentTimeMillis();
byte[] bytes = fst.asByteArray(object);
byte[] bytes = fst.get().asByteArray(object);
long totalTime = System.currentTimeMillis() - beginTime;
if (totalTime > 3000){
System.out.println("Fst serialize time: " + TimeUtils.formatDateAgo(totalTime));
@@ -307,7 +249,7 @@ public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
return null;
}
long beginTime = System.currentTimeMillis();
Object object = fst.asObject(bytes);
Object object = fst.get().asObject(bytes);
long totalTime = System.currentTimeMillis() - beginTime;
if (totalTime > 3000){
System.out.println("Fst unserialize time: " + TimeUtils.formatDateAgo(totalTime));

View File

@@ -250,16 +250,14 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
if (s == null) {
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == SEPARATOR) {
upperCase = true;
upperCase = i != 1; // 不允许第二个字符是大写
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
@@ -267,7 +265,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
sb.append(c);
}
}
return sb.toString();
}
@@ -297,18 +294,14 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
if (s == null) {
return null;
}
StringBuilder sb = new StringBuilder();
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean nextUpperCase = true;
if (i < (s.length() - 1)) {
nextUpperCase = Character.isUpperCase(s.charAt(i + 1));
}
if ((i > 0) && Character.isUpperCase(c)) {
if (!upperCase || !nextUpperCase) {
sb.append(SEPARATOR);
@@ -317,10 +310,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
} else {
upperCase = false;
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}

View File

@@ -3,13 +3,10 @@
*/
package com.jeesite.common.mail;
import org.apache.commons.mail.HtmlEmail;
import com.jeesite.common.io.PropertiesUtils;
/**
* 发送电子邮件
*/
@Deprecated
public class EmailUtils {
/**
@@ -19,14 +16,9 @@ public class EmailUtils {
* @param content 内容
* @return
*/
@Deprecated
public static boolean sendEmail(String toAddress, String subject, String content) {
PropertiesUtils loader = PropertiesUtils.getInstance();
String fromAddress = loader.getProperty("msg.email.fromAddress");
String fromPassword = loader.getProperty("msg.email.fromPassword");
String fromHostName = loader.getProperty("msg.email.fromHostName");
String sslOnConnect = loader.getProperty("msg.email.sslOnConnect", "false");
String sslSmtpPort = loader.getProperty("msg.email.sslSmtpPort");
return sendEmail(fromAddress, fromPassword, fromHostName, sslOnConnect, sslSmtpPort, toAddress, subject, content);
return com.jeesite.common.msg.EmailUtils.send(toAddress, subject, content);
}
/**
@@ -36,296 +28,10 @@ public class EmailUtils {
* @param content 内容
* @return
*/
@Deprecated
public static boolean sendEmail(String fromAddress, String fromPassword, String fromHostName,
String sslOnConnect, String sslSmtpPort, String toAddress, String subject, String content) {
try {
HtmlEmail htmlEmail = new HtmlEmail();
// 发送地址
htmlEmail.setFrom(fromAddress);
// 密码校验
htmlEmail.setAuthentication(fromAddress, fromPassword);
// 发送服务器协议
htmlEmail.setHostName(fromHostName);
// SSL
if ("true".equals(sslOnConnect)) {
htmlEmail.setSSLOnConnect(true);
htmlEmail.setSslSmtpPort(sslSmtpPort);
}
// 接收地址
htmlEmail.addTo(toAddress);
// 标题
htmlEmail.setSubject(subject);
// 内容
htmlEmail.setMsg(content);
// 其他信息
htmlEmail.setCharset("utf-8");
// 发送
htmlEmail.send();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
return com.jeesite.common.msg.EmailUtils.send(fromAddress, fromPassword, fromHostName, sslOnConnect, sslSmtpPort, toAddress, subject, content);
}
// // private static final String smtphost = "192.168.1.70";
// private static final String from = "thinkgem@163.com";
// private static final String fromName = "测试公司";
// private static final String charSet = "utf-8";
// private static final String username = "thinkgem@163.com";
// private static final String password = "123456";
//
// private static Map<String, String> hostMap = new HashMap<String, String>();
// static {
// // 126
// hostMap.put("smtp.126", "smtp.126.com");
// // qq
// hostMap.put("smtp.qq", "smtp.qq.com");
//
// // 163
// hostMap.put("smtp.163", "smtp.163.com");
//
// // sina
// hostMap.put("smtp.sina", "smtp.sina.com.cn");
//
// // tom
// hostMap.put("smtp.tom", "smtp.tom.com");
//
// // 263
// hostMap.put("smtp.263", "smtp.263.net");
//
// // yahoo
// hostMap.put("smtp.yahoo", "smtp.mail.yahoo.com");
//
// // hotmail
// hostMap.put("smtp.hotmail", "smtp.live.com");
//
// // gmail
// hostMap.put("smtp.gmail", "smtp.gmail.com");
// hostMap.put("smtp.port.gmail", "465");
// }
//
// public static String getHost(String email) throws Exception {
// Pattern pattern = Pattern.compile("\\w+@(\\w+)(\\.\\w+){1,2}");
// Matcher matcher = pattern.matcher(email);
// String key = "unSupportEmail";
// if (matcher.find()) {
// key = "smtp." + matcher.group(1);
// }
// if (hostMap.containsKey(key)) {
// return hostMap.get(key);
// } else {
// throw new Exception("unSupportEmail");
// }
// }
//
// public static int getSmtpPort(String email) throws Exception {
// Pattern pattern = Pattern.compile("\\w+@(\\w+)(\\.\\w+){1,2}");
// Matcher matcher = pattern.matcher(email);
// String key = "unSupportEmail";
// if (matcher.find()) {
// key = "smtp.port." + matcher.group(1);
// }
// if (hostMap.containsKey(key)) {
// return Integer.parseInt(hostMap.get(key));
// } else {
// return 25;
// }
// }
//
// /**
// * 发送模板邮件
// *
// * @param toMailAddr 收信人地址
// * @param subject email主题
// * @param templatePath 模板地址
// * @param map 模板map
// */
// public static void sendFtlMail(String toMailAddr, String subject, String templatePath, Map<String, Object> map) {
// Template template = null;
// Configuration freeMarkerConfig = null;
// HtmlEmail hemail = new HtmlEmail();
// try {
// hemail.setHostName(getHost(from));
// hemail.setSmtpPort(getSmtpPort(from));
// hemail.setCharset(charSet);
// hemail.addTo(toMailAddr);
// hemail.setFrom(from, fromName);
// hemail.setAuthentication(username, password);
// hemail.setSubject(subject);
// freeMarkerConfig = new Configuration();
// freeMarkerConfig.setDirectoryForTemplateLoading(new File(getFilePath()));
// // 获取模板
// template = freeMarkerConfig.getTemplate(getFileName(templatePath), new Locale("Zh_cn"), "UTF-8");
// // 模板内容转换为string
// String htmlText = FreeMarkers.renderTemplate(template, map);
// System.out.println(htmlText);
// hemail.setMsg(htmlText);
// hemail.send();
// System.out.println("email send true!");
// } catch (Exception e) {
// e.printStackTrace();
// System.out.println("email send error!");
// }
// }
//
// /**
// * 发送普通邮件
// *
// * @param toMailAddr 收信人地址
// * @param subject email主题
// * @param message 发送email信息
// */
// public static void sendCommonMail(String toMailAddr, String subject, String message) {
// HtmlEmail hemail = new HtmlEmail();
// try {
// hemail.setHostName(getHost(from));
// hemail.setSmtpPort(getSmtpPort(from));
// hemail.setCharset(charSet);
// hemail.addTo(toMailAddr);
// hemail.setFrom(from, fromName);
// hemail.setAuthentication(username, password);
// hemail.setSubject(subject);
// hemail.setMsg(message);
// hemail.send();
// System.out.println("email send true!");
// } catch (Exception e) {
// e.printStackTrace();
// System.out.println("email send error!");
// }
//
// }
//
// public static String getHtmlText(String templatePath, Map<String, Object> map) {
// Template template = null;
// String htmlText = "";
// try {
// Configuration freeMarkerConfig = null;
// freeMarkerConfig = new Configuration();
// freeMarkerConfig.setDirectoryForTemplateLoading(new File(getFilePath()));
// // 获取模板
// template = freeMarkerConfig.getTemplate(getFileName(templatePath), new Locale("Zh_cn"), "UTF-8");
// // 模板内容转换为string
// htmlText = FreeMarkers.renderTemplate(template, map);
// System.out.println(htmlText);
// } catch (Exception e) {
// e.printStackTrace();
// }
// return htmlText;
// }
//
// private static String getFilePath() {
// String path = getAppPath(SendMailUtil.class);
// path = path + File.separator + "mailtemplate" + File.separator;
// path = path.replace("\\", "/");
// System.out.println(path);
// return path;
// }
//
// private static String getFileName(String path) {
// path = path.replace("\\", "/");
// System.out.println(path);
// return path.substring(path.lastIndexOf("/") + 1);
// }
//
// // @SuppressWarnings("unchecked")
// public static String getAppPath(Class<?> cls) {
// // 检查用户传入的参数是否为空
// if (cls == null)
// throw new java.lang.IllegalArgumentException("参数不能为空!");
// ClassLoader loader = cls.getClassLoader();
// // 获得类的全名,包括包名
// String clsName = cls.getName() + ".class";
// // 获得传入参数所在的包
// Package pack = cls.getPackage();
// String path = "";
// // 如果不是匿名包,将包名转化为路径
// if (pack != null) {
// String packName = pack.getName();
// // 此处简单判定是否是Java基础类库防止用户传入JDK内置的类库
// if (packName.startsWith("java.") || packName.startsWith("javax."))
// throw new java.lang.IllegalArgumentException("不要传送系统类!");
// // 在类的名称中,去掉包名的部分,获得类的文件名
// clsName = clsName.substring(packName.length() + 1);
// // 判定包名是否是简单包名,如果是,则直接将包名转换为路径,
// if (packName.indexOf(".") < 0)
// path = packName + "/";
// else {// 否则按照包名的组成部分,将包名转换为路径
// int start = 0, end = 0;
// end = packName.indexOf(".");
// while (end != -1) {
// path = path + packName.substring(start, end) + "/";
// start = end + 1;
// end = packName.indexOf(".", start);
// }
// path = path + packName.substring(start) + "/";
// }
// }
// // 调用ClassLoader的getResource方法传入包含路径信息的类文件名
// java.net.URL url = loader.getResource(path + clsName);
// // 从URL对象中获取路径信息
// String realPath = url.getPath();
// // 去掉路径信息中的协议名"file:"
// int pos = realPath.indexOf("file:");
// if (pos > -1)
// realPath = realPath.substring(pos + 5);
// // 去掉路径信息最后包含类文件信息的部分,得到类所在的路径
// pos = realPath.indexOf(path + clsName);
// realPath = realPath.substring(0, pos - 1);
// // 如果类文件被打包到JAR等文件中时去掉对应的JAR等打包文件名
// if (realPath.endsWith("!"))
// realPath = realPath.substring(0, realPath.lastIndexOf("/"));
// /*------------------------------------------------------------
// ClassLoader的getResource方法使用了utf-8对路径信息进行了编码当路径
// 中存在中文和空格时,他会对这些字符进行转换,这样,得到的往往不是我们想要
// 的真实路径在此调用了URLDecoder的decode方法进行解码以便得到原始的
// 中文及空格路径
// -------------------------------------------------------------*/
// try {
// realPath = java.net.URLDecoder.decode(realPath, "utf-8");
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// System.out.println("realPath----->" + realPath);
// return realPath;
// }
//
// // private static File getFile(String path){
// // File file =
// // SendMail.class.getClassLoader().getResource("mailtemplate/test.ftl").getFile();
// // return file;
// // }
// //
//
// public static void main(String[] args) {
// // HtmlEmail hemail = new HtmlEmail();
// // try {
// // hemail.setHostName("smtp.exmail.qq.com");
// // hemail.setCharset("utf-8");
// // hemail.addTo("test@qq.com");
// // hemail.setFrom("test@qq.com", "test");
// // hemail.setAuthentication("test@test.com", "test@aa");
// // hemail.setSubject("sendemail test!");
// // hemail.setMsg("<a href=\"http://www.google.cn\">谷歌</a><br/>");
// // hemail.send();
// // System.out.println("email send true!");
// // } catch (Exception e) {
// // e.printStackTrace();
// // System.out.println("email send error!");
// // }
// Map<String, Object> map = new HashMap<String, Object>();
// map.put("subject", "测试标题");
// map.put("content", "测试 内容");
// String templatePath = "mailtemplate/test.ftl";
// sendFtlMail("test@163.com", "sendemail test!", templatePath, map);
//
// // System.out.println(getFileName("mailtemplate/test.ftl"));
// }
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
*/
package com.jeesite.common.msg;
import org.apache.commons.mail.HtmlEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jeesite.common.io.PropertiesUtils;
/**
* 发送电子邮件
*/
public class EmailUtils {
private final static Logger logger = LoggerFactory.getLogger(EmailUtils.class);
/**
* 发送邮件
* @param toAddress 接收地址
* @param subject 标题
* @param content 内容
* @return
*/
public static boolean send(String toAddress, String subject, String content) {
PropertiesUtils loader = PropertiesUtils.getInstance();
String fromAddress = loader.getProperty("msg.email.fromAddress");
String fromPassword = loader.getProperty("msg.email.fromPassword");
String fromHostName = loader.getProperty("msg.email.fromHostName");
String sslOnConnect = loader.getProperty("msg.email.sslOnConnect", "false");
String sslSmtpPort = loader.getProperty("msg.email.sslSmtpPort");
return send(fromAddress, fromPassword, fromHostName, sslOnConnect, sslSmtpPort, toAddress, subject, content);
}
/**
* 发送邮件
* @param toAddress 接收地址
* @param subject 标题
* @param content 内容
* @return
*/
public static boolean send(String fromAddress, String fromPassword, String fromHostName,
String sslOnConnect, String sslSmtpPort, String toAddress, String subject, String content) {
try {
HtmlEmail htmlEmail = new HtmlEmail();
// 发送地址
htmlEmail.setFrom(fromAddress);
// 密码校验
htmlEmail.setAuthentication(fromAddress, fromPassword);
// 发送服务器协议
htmlEmail.setHostName(fromHostName);
// SSL
if ("true".equals(sslOnConnect)) {
htmlEmail.setSSLOnConnect(true);
htmlEmail.setSslSmtpPort(sslSmtpPort);
}
// 接收地址
htmlEmail.addTo(toAddress);
// 标题
htmlEmail.setSubject(subject);
// 内容
htmlEmail.setMsg(content);
// 其他信息
htmlEmail.setCharset("utf-8");
// 发送
htmlEmail.send();
return true;
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return false;
}
}

View File

@@ -0,0 +1,141 @@
package com.jeesite.common.msg;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jeesite.common.io.PropertiesUtils;
import com.jeesite.common.lang.DateUtils;
/**
* 发送短信(乐云短信)
*/
public class SmsUtils {
private final static Logger logger = LoggerFactory.getLogger(SmsUtils.class);
private final static PropertiesUtils props = PropertiesUtils.getInstance();
private final static String url = props.getProperty("msg.sms.url", "http://lehuo520.cn/a/sms/api");
private final static String data = props.getProperty("msg.sms.data", "username=jeesite&password=jeesite.com");
private final static String prefix = props.getProperty("msg.sms.prefix", "【JeeSite】");
private final static String suffix = props.getProperty("msg.sms.suffix", "");
// public static void main(String[] args) {
// String phone = "18500000000"; // 收短信人手机号码;例如:18500000000 支持多号码号码之间用英文逗号隔开最多100个
// String content = "您好您的验证码是123456请勿透露给其他人感谢您的使用。"; // 输入需要发送内容;例如:你好这是一条测试短信
// String smsid = ""; // 短信id查询短信状态报告时需要可为空
// System.out.println(send(content, phone)); // 发短信
// System.out.println(status(smsid, phone)); // 取状态
// System.out.println(reply()); //取上行 回复短信
// }
/**
* 发送短信
* @param content 接受内容
* @param phone 接受手机号码
* @return {"result":"0","describing""提交成功","sms":[{"phone":"18073110001,18073110002","smsid":"83bd18f1d48b4cc9b9fe7810c768ac43","status":"3"}]}
*/
public static String send(String content, String phone) {
return send(content, phone, null);
}
/**
* 发送短信
* @param content 接受内容
* @param phone 接受手机号码
* @param sendtime 发送时间为空立即发送
* @return {"result":"0","describing""提交成功","sms":[{"phone":"18073110001,18073110002","smsid":"83bd18f1d48b4cc9b9fe7810c768ac43","status":"3"}]}
*/
public static String send(String content, String phone, Date sendTime) {
String res = "";
try {
String param = data + "&phone=" + phone + "&content=" + URLEncoder
.encode(prefix + content + suffix, "UTF-8")
+ "&sendTime=" + (sendTime != null ? DateUtils
.formatDate(sendTime, "yyyyMMddHHmm") : "");
res = connectURL(url + "/send", param);
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return res;
}
/**
* 获取状态
* @param smsid,phone可为空,为空取最近两天未获取状态报告,沦询间隔时间不能低于5分钟
* @return 请求错误返回页面示例: {"result":"-1","describing""帐号不存在,请检查用户名或者密码是否正确","sms":[]} 请求成功返回页面示例:
* {"result":"0","describing""提交成功","sms":[{"phone":"18073110001","smsid":"83bd18f1d48b4cc9b9fe7810c768ac43","status":"7"},{"phone":"18073110001","smsid":"83bd18f1d48b4cc9b9fe7810c768ac43","status":"8"}]}
*/
public static String status(String smsid, String phone) {
String res = "";
try {
String param = data + "&smsid=" + smsid;
res = connectURL(url + "/status", param);
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return res;
}
/**
* 获取回复
* @param smsid:下发短信对应短信IDtaskId同一批任务ID
* @return {"result":"0","sms":[{"phone":"18073110001","neirong":"已收到","taskId":"83bd18f1d48b4cc9b9fe7810c768ac43"},"smsId":"83bd18f1d48b48j9b9fe7810c768ac43"}]}
*/
public static String reply() {
String res = "";
try {
String param = data;
res = connectURL(url + "/query", param);
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return res;
}
/**
* 进行http提交
* @param
* @return
* @throws IOException
* @throws Exception
*/
private static String connectURL(String url, String param) throws IOException {
String res = "";
HttpURLConnection urlConn = null;
URL url1 = new URL(url);
urlConn = (HttpURLConnection) url1.openConnection();
urlConn.setRequestMethod("POST");
urlConn.setDoOutput(true);
OutputStream out = null;
BufferedReader rd = null;
try{
out = urlConn.getOutputStream();
out.write(param.getBytes("UTF-8"));
out.flush();
rd = new BufferedReader(new InputStreamReader(urlConn.getInputStream(), "UTF-8"));
StringBuffer sb = new StringBuffer();
int ch;
while ((ch = rd.read()) > -1) {
sb.append((char) ch);
}
res = sb.toString().trim();
}finally {
if (out!=null){
out.close();
}
if (rd!=null){
rd.close();
}
}
return res;
}
}

View File

@@ -2,7 +2,8 @@ package com.jeesite.common.network;
import javax.servlet.http.HttpServletRequest;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.io.PropertiesUtils;
import com.jeesite.common.lang.StringUtils;
public class IpUtils {
@@ -16,20 +17,23 @@ public class IpUtils {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
String ip = null;
String xffName = PropertiesUtils.getInstance()
.getProperty("shiro.remoteAddrHeaderName");
if (StringUtils.isNotBlank(xffName)){
ip = request.getHeader(xffName);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return StringUtils.split(ObjectUtils.toString(ip), ",")[0];
if (StringUtils.isNotBlank(ip)){
ip = EncodeUtils.xssFilter(ip);
ip = StringUtils.split(ip, ",")[0];
}
if (StringUtils.isBlank(ip)){
ip = "unknown";
}
return ip;
}
/**

View File

@@ -76,7 +76,9 @@ public class ReflectUtils {
public static Object getFieldValue(final Object obj, final String fieldName) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
//throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
logger.warn("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return null;
}
Object result = null;
try {
@@ -93,7 +95,9 @@ public class ReflectUtils {
public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
//throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
logger.warn("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return;
}
try {
field.set(obj, value);
@@ -114,7 +118,9 @@ public class ReflectUtils {
}
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
//throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
logger.warn("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
return null;
}
try {
return method.invoke(obj, args);
@@ -132,7 +138,10 @@ public class ReflectUtils {
public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
Method method = getAccessibleMethodByName(obj, methodName, args.length);
if (method == null) {
throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
// 如果为空不报错,直接返回空。
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
logger.warn("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
return null;
}
try {
// 类型转换(将参数数据类型转换为目标方法参数类型)
@@ -174,7 +183,11 @@ public class ReflectUtils {
* 如向上转型到Object仍无法找到, 返回null.
*/
public static Field getAccessibleField(final Object obj, final String fieldName) {
Validate.notNull(obj, "object can't be null");
// 为空不报错。直接返回 null
// Validate.notNull(obj, "object can't be null");
if (obj == null){
return null;
}
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
@@ -197,7 +210,11 @@ public class ReflectUtils {
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes) {
Validate.notNull(obj, "object can't be null");
// 为空不报错。直接返回 null
// Validate.notNull(obj, "object can't be null");
if (obj == null){
return null;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
try {
@@ -219,7 +236,11 @@ public class ReflectUtils {
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) {
Validate.notNull(obj, "object can't be null");
// 为空不报错。直接返回 null
// Validate.notNull(obj, "object can't be null");
if (obj == null){
return null;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
Method[] methods = searchType.getDeclaredMethods();

View File

@@ -1,9 +1,12 @@
package com.jeesite.common.text;
import java.util.regex.Pattern;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
/**
@@ -12,34 +15,16 @@ import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombi
*/
public class PinyinUtils {
// /**
// * 将字符串中的中文转化为拼音,其他字符不变
// * @param inputString
// * @return
// */
// public static String getPinyin(String inputString) {
// HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
// format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
// format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
// format.setVCharType(HanyuPinyinVCharType.WITH_V);
//
// char[] input = inputString.trim().toCharArray();
// String output = "";
//
// try {
// for (int i = 0; i < input.length; i++) {
// if (java.lang.Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) {
// String[] temp = PinyinHelper.toHanyuPinyinStringArray(input[i], format);
// output += temp[0];
// } else {
// output += java.lang.Character.toString(input[i]);
// }
// }
// } catch (BadHanyuPinyinOutputFormatCombination e) {
// e.printStackTrace();
// }
// return output;
// }
private static class Static{
private static Pattern idPatt = Pattern.compile("\\W");
private static HanyuPinyinOutputFormat defaultFormat;
static{
defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
defaultFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
}
}
/**
* 获取汉字串拼音首字母,替换调非法标示符字符,英文字符不变,去除空格
@@ -47,17 +32,30 @@ public class PinyinUtils {
* @return 汉语拼音首字母
*/
public static String getFirstSpell(String chinese) {
return getFirstSpell(chinese, true);
}
/**
* 获取汉字串拼音首字母,替换调非法标示符字符,英文字符不变,去除空格
* @param chinese 汉字串
* @param isId 是否标示符true将去掉特殊字符
* @return 汉语拼音首字母
*/
public static String getFirstSpell(String chinese, boolean isId) {
chinese = getDbc(chinese);
if (chinese == null){
return null;
}
StringBuffer pybf = new StringBuffer();
char[] arr = chinese.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < arr.length; i++) {
if (arr[i] > 128) {
try {
String[] temp = PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat);
String[] temp = PinyinHelper.toHanyuPinyinStringArray(arr[i], Static.defaultFormat);
if (temp != null) {
pybf.append(temp[0].charAt(0));
}else{
pybf.append(String.valueOf(arr[i]));
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
@@ -66,7 +64,10 @@ public class PinyinUtils {
pybf.append(arr[i]);
}
}
return pybf.toString().replaceAll("\\W", "").trim();
if (isId){
return Static.idPatt.matcher(pybf.toString()).replaceAll("").trim();
}
return pybf.toString();
}
/**
@@ -75,17 +76,30 @@ public class PinyinUtils {
* @return 汉语拼音
*/
public static String getFullSpell(String chinese) {
return getFullSpell(chinese, true);
}
/**
* 获取汉字串全拼,英文字符不变
* @param chinese 汉字串
* @param isId 是否标示符true将去掉特殊字符
* @return 汉语拼音
*/
public static String getFullSpell(String chinese, boolean isId) {
chinese = getDbc(chinese);
if (chinese == null){
return null;
}
StringBuffer pybf = new StringBuffer();
char[] arr = chinese.toCharArray();
HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < arr.length; i++) {
if (arr[i] > 128) {
try {
String[] ss = PinyinHelper.toHanyuPinyinStringArray(arr[i], defaultFormat);
String[] ss = PinyinHelper.toHanyuPinyinStringArray(arr[i], Static.defaultFormat);
if (ss != null && ss.length > 0){
pybf.append(ss[0]);
}else{
pybf.append(String.valueOf(arr[i]));
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
@@ -94,13 +108,57 @@ public class PinyinUtils {
pybf.append(arr[i]);
}
}
if (isId){
return Static.idPatt.matcher(pybf.toString()).replaceAll("").trim();
}
return pybf.toString();
}
/**
* 半角转全角
* @param input String.
* @return 全角字符串.
*/
public static String getSbc(String input) {
if (input == null){
return null;
}
char c[] = input.toCharArray();
for (int i = 0; i < c.length; i++) {
if (c[i] == ' ') {
c[i] = '\u3000';
} else if (c[i] < '\177') {
c[i] = (char) (c[i] + 65248);
}
}
return new String(c);
}
/**
* 全角转半角
* @param input String.
* @return 半角字符串
*/
public static String getDbc(String input) {
if (input == null){
return null;
}
char c[] = input.toCharArray();
for (int i = 0; i < c.length; i++) {
if (c[i] == '\u3000') {
c[i] = ' ';
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
c[i] = (char) (c[i] - 65248);
}
}
return new String(c);
}
// public static void main(String[] args) {
// String str = "你好123世界abc,~!#$_Sdf";
//// System.out.println(getPinyin(str));
// String str = "你好123世界abc,~!#$_Sdf,女;hello!-";
// System.out.println(getFirstSpell(str));
// System.out.println(getFirstSpell(str, false));
// System.out.println(getFullSpell(str));
// System.out.println(getFullSpell(str, false));
// }
}

View File

@@ -3,6 +3,7 @@
*/
package com.jeesite.common.utils.excel;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -13,8 +14,10 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
@@ -36,6 +39,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.collect.SetUtils;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.reflect.ReflectUtils;
@@ -47,9 +51,9 @@ import com.jeesite.common.utils.excel.annotation.ExcelFields;
/**
* 导出Excel文件导出“XLSX”格式支持大数据量导出 @see org.apache.poi.ss.SpreadsheetVersion
* @author ThinkGem
* @version 2013-04-21
* @version 2018-08-11
*/
public class ExcelExport {
public class ExcelExport implements Closeable{
private static Logger log = LoggerFactory.getLogger(ExcelExport.class);
@@ -76,7 +80,12 @@ public class ExcelExport {
/**
* 注解列表Object[]{ ExcelField, Field/Method }
*/
List<Object[]> annotationList;
private List<Object[]> annotationList;
/**
* 用于清理缓存
*/
private Set<Class<?>> fieldTypes = SetUtils.newHashSet();
/**
* 构造函数
@@ -431,6 +440,7 @@ public class ExcelExport {
if(val == null){
cell.setCellValue("");
}else if(fieldType != Class.class){
fieldTypes.add(fieldType); // 先存起来,方便完成后清理缓存
cell.setCellValue((String)fieldType.getMethod("setValue", Object.class).invoke(null, val));
try{
defaultDataFormat = (String)fieldType.getMethod("getDataFormat").invoke(null);
@@ -456,8 +466,11 @@ public class ExcelExport {
cell.setCellValue((Date) val);
defaultDataFormat = "yyyy-MM-dd HH:mm";
}else {
cell.setCellValue((String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+val.getClass().getSimpleName()+"Type")).getMethod("setValue", Object.class).invoke(null, val));
// 如果没有指定 fieldType切自行根据类型查找相应的转换类com.jeesite.common.utils.excel.fieldtype.值的类名+Type
Class<?> fieldType2 = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+val.getClass().getSimpleName()+"Type"));
fieldTypes.add(fieldType2); // 先存起来,方便完成后清理缓存
cell.setCellValue((String)fieldType2.getMethod("setValue", Object.class).invoke(null, val));
}
}
// if (val != null){
@@ -574,12 +587,31 @@ public class ExcelExport {
/**
* 清理临时文件
* @deprecated see close()
*/
public ExcelExport dispose(){
try {
this.close();
} catch (Exception e) {
e.printStackTrace();
}
return this;
}
@Override
public void close() {
if (wb instanceof SXSSFWorkbook){
((SXSSFWorkbook)wb).dispose();
}
return this;
Iterator<Class<?>> it = fieldTypes.iterator();
while(it.hasNext()){
Class<?> clazz = it.next();
try {
clazz.getMethod("clearCache").invoke(null);
} catch (Exception e) {
// 报错忽略,有可能没实现此方法
}
}
}
// /**

View File

@@ -3,6 +3,7 @@
*/
package com.jeesite.common.utils.excel;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -15,6 +16,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
@@ -34,6 +36,7 @@ import org.springframework.web.multipart.MultipartFile;
import com.jeesite.common.callback.MethodCallback;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.collect.SetUtils;
import com.jeesite.common.lang.DateUtils;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.lang.StringUtils;
@@ -45,9 +48,9 @@ import com.jeesite.common.utils.excel.annotation.ExcelFields;
/**
* 导入Excel文件支持“XLS”和“XLSX”格式
* @author ThinkGem
* @version 2014-8-19
* @version 2018-08-11
*/
public class ExcelImport {
public class ExcelImport implements Closeable {
private static Logger log = LoggerFactory.getLogger(ExcelImport.class);
@@ -66,6 +69,11 @@ public class ExcelImport {
*/
private int headerNum;
/**
* 用于清理缓存
*/
private Set<Class<?>> fieldTypes = SetUtils.newHashSet();
/**
* 构造函数
* @param path 导入文件对象,读取第一个工作表
@@ -418,6 +426,7 @@ public class ExcelImport {
try {
if (StringUtils.isNotBlank(ef.attrName())){
if (ef.fieldType() != Class.class){
fieldTypes.add(ef.fieldType()); // 先存起来,方便完成后清理缓存
val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val);
}
}else{
@@ -445,10 +454,14 @@ public class ExcelImport {
}
}else{
if (ef.fieldType() != Class.class){
fieldTypes.add(ef.fieldType()); // 先存起来,方便完成后清理缓存
val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val.toString());
}else{
val = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+valType.getSimpleName()+"Type")).getMethod("getValue", String.class).invoke(null, val.toString());
// 如果没有指定 fieldType切自行根据类型查找相应的转换类com.jeesite.common.utils.excel.fieldtype.值的类名+Type
Class<?> fieldType2 = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+valType.getSimpleName()+"Type"));
fieldTypes.add(fieldType2); // 先存起来,方便完成后清理缓存
val = fieldType2.getMethod("getValue", String.class).invoke(null, val.toString());
}
}
}
@@ -481,6 +494,19 @@ public class ExcelImport {
}
return dataList;
}
@Override
public void close() {
Iterator<Class<?>> it = fieldTypes.iterator();
while(it.hasNext()){
Class<?> clazz = it.next();
try {
clazz.getMethod("clearCache").invoke(null);
} catch (Exception e) {
// 报错忽略,有可能没实现此方法
}
}
}
// /**
// * 导入测试

View File

@@ -37,4 +37,11 @@ public class MoneyType {
return "0.00";
}
/**
* 清理缓存
*/
public static void clearCache(){
}
}

View File

@@ -11,8 +11,8 @@
</page_setting>
<category_index>0</category_index>
<zoom>1.0</zoom>
<x>2660</x>
<y>94</y>
<x>1503</x>
<y>1793</y>
<default_color>
<r>128</r>
<g>128</g>
@@ -12354,6 +12354,10 @@
<type></type>
<description></description>
<columns>
<column>
<id>abd5fc5f0bb213daae3de2abc362c5aea5276f4d</id>
<desc>false</desc>
</column>
</columns>
</inidex>
</indexes>
@@ -13783,6 +13787,10 @@
<type></type>
<description></description>
<columns>
<column>
<id>bc518c6e08246711da4ef4bbfd902690ebf6797d</id>
<desc>false</desc>
</column>
</columns>
</inidex>
</indexes>
@@ -13925,7 +13933,7 @@
<default_value></default_value>
<auto_increment>false</auto_increment>
<foreign_key>false</foreign_key>
<not_null>true</not_null>
<not_null>false</not_null>
<primary_key>false</primary_key>
<unique_key>false</unique_key>
<character_set></character_set>

View File

@@ -175,7 +175,7 @@ CREATE TABLE [js_sys_config]
[id] varchar(64) NOT NULL,
[config_name] nvarchar(100) NOT NULL,
[config_key] varchar(100) NOT NULL,
[config_value] nvarchar(1000) NOT NULL,
[config_value] nvarchar(1000),
[is_sys] char(1) NOT NULL,
[create_by] varchar(64) NOT NULL,
[create_date] datetime NOT NULL,
@@ -788,8 +788,8 @@ CREATE TABLE [js_sys_user_role]
/* Create Indexes */
CREATE INDEX [idx_gen_table_ptn] ON [js_gen_table] ();
CREATE INDEX [idx_gen_table_column_tn] ON [js_gen_table_column] ();
CREATE INDEX [idx_gen_table_ptn] ON [js_gen_table] ([parent_table_name]);
CREATE INDEX [idx_gen_table_column_tn] ON [js_gen_table_column] ([table_name]);
CREATE INDEX [idx_sys_area_pc] ON [js_sys_area] ([parent_code]);
CREATE INDEX [idx_sys_area_ts] ON [js_sys_area] ([tree_sort]);
CREATE INDEX [idx_sys_area_status] ON [js_sys_area] ([status]);

View File

@@ -176,7 +176,7 @@ CREATE TABLE js_sys_config
id varchar(64) NOT NULL COMMENT '编号',
config_name varchar(100) NOT NULL COMMENT '名称',
config_key varchar(100) NOT NULL COMMENT '参数键',
config_value varchar(1000) NOT NULL COMMENT '参数值',
config_value varchar(1000) COMMENT '参数值',
is_sys char(1) NOT NULL COMMENT '系统内置1是 0否',
create_by varchar(64) NOT NULL COMMENT '创建者',
create_date datetime NOT NULL COMMENT '创建时间',
@@ -790,8 +790,8 @@ CREATE TABLE js_sys_user_role
/* Create Indexes */
CREATE INDEX idx_gen_table_ptn ON js_gen_table ();
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column ();
CREATE INDEX idx_gen_table_ptn ON js_gen_table (parent_table_name ASC);
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column (table_name ASC);
CREATE INDEX idx_sys_area_pc ON js_sys_area (parent_code ASC);
CREATE INDEX idx_sys_area_ts ON js_sys_area (tree_sort ASC);
CREATE INDEX idx_sys_area_status ON js_sys_area (status ASC);

View File

@@ -175,7 +175,7 @@ CREATE TABLE js_sys_config
id varchar2(64) NOT NULL,
config_name nvarchar2(100) NOT NULL,
config_key varchar2(100) NOT NULL,
config_value nvarchar2(1000) NOT NULL,
config_value nvarchar2(1000),
is_sys char(1) NOT NULL,
create_by varchar2(64) NOT NULL,
create_date timestamp NOT NULL,
@@ -788,8 +788,8 @@ CREATE TABLE js_sys_user_role
/* Create Indexes */
CREATE INDEX idx_gen_table_ptn ON js_gen_table ();
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column ();
CREATE INDEX idx_gen_table_ptn ON js_gen_table (parent_table_name);
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column (table_name);
CREATE INDEX idx_sys_area_pc ON js_sys_area (parent_code);
CREATE INDEX idx_sys_area_ts ON js_sys_area (tree_sort);
CREATE INDEX idx_sys_area_status ON js_sys_area (status);

View File

@@ -175,7 +175,7 @@ CREATE TABLE js_sys_config
id varchar(64) NOT NULL,
config_name varchar(100) NOT NULL,
config_key varchar(100) NOT NULL,
config_value varchar(1000) NOT NULL,
config_value varchar(1000),
is_sys char(1) NOT NULL,
create_by varchar(64) NOT NULL,
create_date timestamp NOT NULL,
@@ -788,8 +788,8 @@ CREATE TABLE js_sys_user_role
/* Create Indexes */
CREATE INDEX idx_gen_table_ptn ON js_gen_table ();
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column ();
CREATE INDEX idx_gen_table_ptn ON js_gen_table (parent_table_name);
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column (table_name);
CREATE INDEX idx_sys_area_pc ON js_sys_area (parent_code);
CREATE INDEX idx_sys_area_ts ON js_sys_area (tree_sort);
CREATE INDEX idx_sys_area_status ON js_sys_area (status);

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>4.0.4-SNAPSHOT</version>
<version>4.0.6-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>

View File

@@ -8,6 +8,7 @@ import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
@@ -15,17 +16,18 @@ import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
import com.jeesite.common.shiro.realm.LoginInfo;
/**
* CAS过滤器
* @author ThinkGem
* @version 2017-03-22
* @version 2018-7-11
*/
@SuppressWarnings("deprecation")
public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter {
private BaseAuthorizingRealm authorizingRealm; // 安全认证类
private CasAuthorizingRealm authorizingRealm; // 安全认证类
/**
* 登录成功调用事件
@@ -34,8 +36,8 @@ public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter {
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
// 登录成功后初始化授权信息并处理登录后的操作
authorizingRealm.onLoginSuccess(subject.getPrincipals());
authorizingRealm.onLoginSuccess((LoginInfo)subject.getPrincipal(), (HttpServletRequest)request);
String url = request.getParameter("__url");
if (StringUtils.isNotBlank(url)) {
WebUtils.issueRedirect(request, response, url, null, true);
@@ -80,7 +82,7 @@ public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter {
}
}
public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) {
public void setAuthorizingRealm(CasAuthorizingRealm authorizingRealm) {
this.authorizingRealm = authorizingRealm;
}

View File

@@ -23,18 +23,21 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jeesite.common.codec.DesUtils;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.network.IpUtils;
import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
import com.jeesite.common.shiro.realm.LoginInfo;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.utils.UserUtils;
/**
* 表单验证(包含验证码)过滤类
* @author ThinkGem
* @version 2017-03-22
* @version 2018-7-11
*/
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
@@ -92,7 +95,7 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
}
// 登录成功后,判断是否需要记住用户名
if (WebUtils.isTrue(request, DEFAULT_REMEMBER_USERCODE_PARAM)) {
rememberUserCodeCookie.setValue(username);
rememberUserCodeCookie.setValue(EncodeUtils.xssFilter(username));
rememberUserCodeCookie.saveTo((HttpServletRequest)request, (HttpServletResponse)response);
} else {
rememberUserCodeCookie.removeFrom((HttpServletRequest)request, (HttpServletResponse)response);
@@ -156,6 +159,9 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
return captcha;
}
/**
* 跳转登录页时,跳转到默认首页
*/
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsAuthorizationFilter.redirectToDefaultPath(request, response);
@@ -207,6 +213,18 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
boolean isLogin = WebUtils.isTrue(request, "__login");
return super.isLoginSubmission(request, response) || isLogin;
}
/**
* 执行登录方法
*/
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
// 是否在登录后生成新的Session默认false
if (Global.getPropertyToBoolean("shiro.isGenerateNewSessionAfterLogin", "false")){
UserUtils.getSubject().logout();
}
return super.executeLogin(request, response);
}
/**
* 登录成功调用事件
@@ -215,7 +233,7 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
// 登录成功后初始化授权信息并处理登录后的操作
authorizingRealm.onLoginSuccess(subject.getPrincipals());
authorizingRealm.onLoginSuccess((LoginInfo)subject.getPrincipal(), (HttpServletRequest) request);
// 登录操作如果是Ajax操作直接返回登录信息字符串。
if (ServletUtils.isAjaxRequest((HttpServletRequest) request)) {

View File

@@ -15,6 +15,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jeesite.common.config.Global;
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
import com.jeesite.common.shiro.realm.LoginInfo;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.Log;
import com.jeesite.modules.sys.utils.LogUtils;
@@ -28,6 +30,7 @@ import com.jeesite.modules.sys.utils.UserUtils;
public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter {
private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class);
private BaseAuthorizingRealm authorizingRealm; // 安全认证类
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
@@ -36,9 +39,19 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter
String redirectUrl = getRedirectUrl(request, response, subject);
//try/catch added for SHIRO-298:
try {
// 记录用户退出日志
LogUtils.saveLog(UserUtils.getUser(), ServletUtils.getRequest(),
"系统退出", Log.TYPE_LOGIN_LOGOUT);
Object principal = subject.getPrincipal();
if (principal != null){
// 记录用户退出日志(@Deprecated v4.0.5支持setAuthorizingRealm之后版本可删除此if子句
if (authorizingRealm == null){
LogUtils.saveLog(UserUtils.getUser(), ServletUtils.getRequest(),
"系统退出", Log.TYPE_LOGIN_LOGOUT);
}
// 退出成功之前初始化授权信息并处理登录后的操作
else{
authorizingRealm.onLogoutSuccess((LoginInfo)subject.getPrincipal(),
(HttpServletRequest)request);
}
}
// 退出登录
subject.logout();
} catch (SessionException ise) {
@@ -71,5 +84,9 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter
}
return super.getRedirectUrl(request, response, subject);
}
public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) {
this.authorizingRealm = authorizingRealm;
}
}

View File

@@ -3,20 +3,11 @@
*/
package com.jeesite.common.shiro.realm;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.subject.PrincipalCollection;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.EmpUser;
import com.jeesite.modules.sys.entity.Log;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.EmpUserService;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
@@ -24,48 +15,35 @@ import com.jeesite.modules.sys.utils.UserUtils;
/**
* 系统安全认证实现类
* @author ThinkGem
* @version 2017-03-22
* @version 2018-7-11
*/
public class AuthorizingRealm extends com.jeesite.common.shiro.realm.BaseAuthorizingRealm {
public class AuthorizingRealm extends BaseAuthorizingRealm {
private UserService userService;
private EmpUserService empUserService;
public AuthorizingRealm() {
super();
}
@Override
protected void casCreateEmpUser(User user, Map<String, Object> attributes) {
EmpUser empUser = new EmpUser();
empUser.setIsNewRecord(true);
empUser.setMobile(user.getMobile());
empUser.setEmail(user.getEmail());
empUser.setPhone(user.getPhone());
empUser.getEmployee().getCompany().setCompanyCode(EncodeUtils
.decodeUrl(ObjectUtils.toString(attributes.get("companyCode"))));
empUser.getEmployee().getOffice().setOfficeCode(EncodeUtils
.decodeUrl(ObjectUtils.toString(attributes.get("officeCode"))));
getEmpUserService().save(empUser);
}
@Override
public void onLoginSuccess(PrincipalCollection principals) {
super.onLoginSuccess(principals);
User user = UserUtils.getUser();
public void onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) {
super.onLoginSuccess(loginInfo, request);
// 更新登录IP、时间、会话ID等
User user = UserUtils.get(loginInfo.getId());
getUserService().updateUserLoginInfo(user);
// 记录用户登录日志
LogUtils.saveLog(user, ServletUtils.getRequest(), "系统登录", Log.TYPE_LOGIN_LOGOUT);
LogUtils.saveLog(user, request, "系统登录", Log.TYPE_LOGIN_LOGOUT);
}
@Override
public void onLogoutSuccess(User logoutUser, HttpServletRequest request) {
public void onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) {
super.onLogoutSuccess(loginInfo, request);
// 记录用户退出日志
LogUtils.saveLog(logoutUser, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
User user = UserUtils.get(loginInfo.getId());
LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
}
public UserService getUserService() {
@@ -74,12 +52,5 @@ public class AuthorizingRealm extends com.jeesite.common.shiro.realm.BaseAuthori
}
return userService;
}
public EmpUserService getEmpUserService() {
if (empUserService == null){
empUserService = SpringUtils.getBean(EmpUserService.class);
}
return empUserService;
}
}

View File

@@ -0,0 +1,242 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
*/
package com.jeesite.common.shiro.realm;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.cas.CasToken;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import com.beust.jcommander.internal.Maps;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.cas.CasCreateUser;
import com.jeesite.common.shiro.cas.CasOutHandler;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.EmpUser;
import com.jeesite.modules.sys.entity.Log;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.EmpUserService;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
/**
* 系统安全认证实现类
* @author ThinkGem
* @version 2018-7-11
*/
@SuppressWarnings("deprecation")
public class CasAuthorizingRealm extends BaseAuthorizingRealm {
private UserService userService;
private EmpUserService empUserService;
///////////// CAS /////////////
private CasOutHandler casOutHandler;
private String casServerUrl; // CAS 服务器地址
private String casServerCallbackUrl; // CAS 服务器回调地址
private TicketValidator ticketValidator;// CAS 令牌验证类
public CasAuthorizingRealm() {
super();
this.setAuthenticationTokenClass(CasToken.class);
}
/*
* 获取登录令牌
*/
@Override
protected FormToken getFormToken(AuthenticationToken authcToken) {
// 单点登录登出句柄登出时注销session有CAS中央服务器调用
HttpServletRequest request = ServletUtils.getRequest();
if (casOutHandler.isLogoutRequest(request)) {
LoginInfo loginInfo = casOutHandler.destroySession(request);
if (loginInfo != null){
this.onLogoutSuccess(loginInfo, request);
}
return null;
}
if (authcToken == null){
return null;
}
CasToken casToken = (CasToken) authcToken;
String ticket = (String) casToken.getCredentials();
if (ticketValidator == null) {
ticketValidator = new Cas20ServiceTicketValidator(casServerUrl);
((Cas20ServiceTicketValidator)ticketValidator).setEncoding("UTF-8");
}
// 进行登录身份验证
Assertion casAssertion = null;
try {
casAssertion = ticketValidator.validate(ticket, casServerCallbackUrl);
} catch (TicketValidationException e) {
// 令牌失效在LogoutFilter会自动跳转到登录页
return null;
}
AttributePrincipal casPrincipal = casAssertion.getPrincipal();
casToken.setUserId(casPrincipal.getName());
// 生成登录信息对象
FormToken token = new FormToken();
token.setUsername(casPrincipal.getName());
Map<String, Object> params = Maps.newHashMap();
params.putAll(casPrincipal.getAttributes());
params.put("ticket", ticket);
token.setParams(params);
return token;
}
/*
* 获取用户信息
*/
@Override
protected User getUserInfo(FormToken token) {
User user = super.getUserInfo(token);
if (user == null){
Map<String, Object> attrs = token.getParams();
// 如果允许客户端创建账号,则创建账号
if (ObjectUtils.toBoolean(attrs.get("isAllowClientCreateUser"))){
// 获取CAS传递过来的用户属性信息
user = new User(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("userCode"))));
user.setLoginCode(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("loginCode"))));
user.setPassword(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("password"))));
user.setUserName(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("userName"))));
user.setEmail(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("email"))));
user.setMobile(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("mobile"))));
user.setPhone(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("phone"))));
user.setUserType(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("userType"))));
user.setRefCode(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("refCode"))));
user.setRefName(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("refName"))));
user.setMgrType(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("mgrType"))));
user.setStatus(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("status"))));
// 如果是员工类型,则平台自动创建
if (User.USER_TYPE_EMPLOYEE.equals(user.getUserType())){
// 保存员工和用户
try{
EmpUser empUser = new EmpUser();
empUser.setIsNewRecord(true);
empUser.setMobile(user.getMobile());
empUser.setEmail(user.getEmail());
empUser.setPhone(user.getPhone());
empUser.getEmployee().getCompany().setCompanyCode(EncodeUtils
.decodeUrl(ObjectUtils.toString(attrs.get("companyCode"))));
empUser.getEmployee().getOffice().setOfficeCode(EncodeUtils
.decodeUrl(ObjectUtils.toString(attrs.get("officeCode"))));
getEmpUserService().save(empUser);
}catch(ValidationException ve){
throw new AuthenticationException("msg:" + ve.getMessage());
}
// 重新获取用户登录
user = UserUtils.getByLoginCode(token.getUsername()/*, corpCode*/);
if (user != null) {
return user;
}
}
// 其它类型,根据项目需要自行创建
else{
try{
CasCreateUser casCreateUser = SpringUtils.getBean(CasCreateUser.class);
if(casCreateUser != null){
casCreateUser.createUser(user, attrs);
}
}catch(NoSuchBeanDefinitionException e){
throw new AuthenticationException("msg:用户 “" + token.getUsername()
+ "”, 类型 “" + user.getUserType() + "” 在本系统中不存在, 请联系管理员.");
}
}
}else{
throw new AuthenticationException("msg:用户 “" + token.getUsername() + "” 在本系统中不存在, 请联系管理员.");
}
}
return user;
}
/*
* 认证密码匹配调用方法
*/
@Override
protected void assertCredentialsMatch(AuthenticationToken authcToken,
AuthenticationInfo info) throws AuthenticationException {
// CAS的Ticket已经在doGetAuthenticationInfo()认证过了,这里就不验证身份了
}
@Override
public void onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) {
super.onLoginSuccess(loginInfo, request);
// 单点登录登出句柄登录时注入session在这之前必须获取下授权信息
String ticket = loginInfo.getParam("ticket");
casOutHandler.recordSession(request, ticket);
//System.out.print("__sid: "+request.getSession().getId());
//System.out.println(" == "+UserUtils.getSession().getId());
// 更新登录IP、时间、会话ID等
User user = UserUtils.get(loginInfo.getId());
getUserService().updateUserLoginInfo(user);
// 记录用户登录日志
LogUtils.saveLog(user, ServletUtils.getRequest(), "系统登录", Log.TYPE_LOGIN_LOGOUT);
}
@Override
public void onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) {
super.onLogoutSuccess(loginInfo, request);
// 记录用户退出日志
User user = UserUtils.get(loginInfo.getId());
LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
}
public UserService getUserService() {
if (userService == null){
userService = SpringUtils.getBean(UserService.class);
}
return userService;
}
public EmpUserService getEmpUserService() {
if (empUserService == null){
empUserService = SpringUtils.getBean(EmpUserService.class);
}
return empUserService;
}
public void setCasOutHandler(CasOutHandler casOutHandler) {
this.casOutHandler = casOutHandler;
}
public void setCasServerUrl(String casServerUrl) {
this.casServerUrl = casServerUrl;
}
public void setCasServerCallbackUrl(String casServerCallbackUrl) {
this.casServerCallbackUrl = casServerCallbackUrl;
}
}

View File

@@ -5,6 +5,8 @@ package com.jeesite.common.utils.excel.fieldtype;
import java.util.List;
import org.springframework.core.NamedThreadLocal;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.modules.sys.entity.Area;
import com.jeesite.modules.sys.utils.AreaUtils;
@@ -12,12 +14,12 @@ import com.jeesite.modules.sys.utils.AreaUtils;
/**
* 字段类型转换
* @author ThinkGem
* @version 2013-03-10
* @version 2018-08-11
* @example fieldType = AreaType.class
*/
public class AreaType {
private static ThreadLocal<List<Area>> cache = new ThreadLocal<>();
private static ThreadLocal<List<Area>> cache = new NamedThreadLocal<>("AreaType");
/**
* 获取对象值(导入)
@@ -45,4 +47,12 @@ public class AreaType {
}
return "";
}
/**
* 清理缓存
*/
public static void clearCache(){
cache.remove();
}
}

View File

@@ -5,6 +5,8 @@ package com.jeesite.common.utils.excel.fieldtype;
import java.util.List;
import org.springframework.core.NamedThreadLocal;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.modules.sys.entity.Company;
import com.jeesite.modules.sys.utils.EmpUtils;
@@ -12,12 +14,12 @@ import com.jeesite.modules.sys.utils.EmpUtils;
/**
* 字段类型转换
* @author ThinkGem
* @version 2015-03-24
* @version 2018-08-11
* @example fieldType = CompanyType.class
*/
public class CompanyType {
private static ThreadLocal<List<Company>> cache = new ThreadLocal<>();
private static ThreadLocal<List<Company>> cache = new NamedThreadLocal<>("CompanyType");
/**
* 获取对象值(导入)
@@ -45,4 +47,11 @@ public class CompanyType {
}
return "";
}
/**
* 清理缓存
*/
public static void clearCache(){
cache.remove();
}
}

View File

@@ -5,6 +5,8 @@ package com.jeesite.common.utils.excel.fieldtype;
import java.util.List;
import org.springframework.core.NamedThreadLocal;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.modules.sys.entity.Office;
import com.jeesite.modules.sys.utils.EmpUtils;
@@ -12,12 +14,12 @@ import com.jeesite.modules.sys.utils.EmpUtils;
/**
* 字段类型转换
* @author ThinkGem
* @version 2013-03-10
* @version 2018-08-11
* @example fieldType = OfficeType.class
*/
public class OfficeType {
private static ThreadLocal<List<Office>> cache = new ThreadLocal<>();
private static ThreadLocal<List<Office>> cache = new NamedThreadLocal<>("OfficeType");
/**
* 获取对象值(导入)
@@ -45,4 +47,11 @@ public class OfficeType {
}
return "";
}
/**
* 清理缓存
*/
public static void clearCache(){
cache.remove();
}
}

View File

@@ -5,6 +5,8 @@ package com.jeesite.common.utils.excel.fieldtype;
import java.util.List;
import org.springframework.core.NamedThreadLocal;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.utils.SpringUtils;
@@ -14,13 +16,13 @@ import com.jeesite.modules.sys.service.PostService;
/**
* 字段类型转换
* @author ThinkGem
* @version 2015-03-24
* @version 2018-08-11
* @example fieldType = PostListType.class
*/
public class PostListType {
private static PostService postService = SpringUtils.getBean(PostService.class);
private static ThreadLocal<List<Post>> cache = new ThreadLocal<>();
private static ThreadLocal<List<Post>> cache = new NamedThreadLocal<>("PostListType");
/**
* 获取对象值(导入)
@@ -53,4 +55,11 @@ public class PostListType {
}
return "";
}
/**
* 清理缓存
*/
public static void clearCache(){
cache.remove();
}
}

View File

@@ -3,19 +3,25 @@
*/
package com.jeesite.modules.config;
import java.util.Collection;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.shiro.cas.CasOutHandler;
import com.jeesite.common.shiro.config.FilterChainDefinitionMap;
@@ -26,6 +32,7 @@ import com.jeesite.common.shiro.filter.PermissionsAuthorizationFilter;
import com.jeesite.common.shiro.filter.RolesAuthorizationFilter;
import com.jeesite.common.shiro.filter.UserFilter;
import com.jeesite.common.shiro.realm.AuthorizingRealm;
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
import com.jeesite.common.shiro.session.SessionDAO;
import com.jeesite.common.shiro.session.SessionManager;
import com.jeesite.common.shiro.web.ShiroFilterFactoryBean;
@@ -34,40 +41,32 @@ import com.jeesite.common.shiro.web.WebSecurityManager;
/**
* Shiro配置
* @author ThinkGem
* @version 2017年11月30日
* @version 2018-7-11
*/
@SuppressWarnings("deprecation")
@Configuration
public class ShiroConfig {
/**
* 单点登录信息句柄,单点退出用
* Apache Shiro Filter
* @throws Exception
*/
@Bean
public CasOutHandler casOutHandler() {
return new CasOutHandler();
}
/**
* 系统安全认证实现类
*/
@Bean
public AuthorizingRealm authorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) {
AuthorizingRealm bean = new AuthorizingRealm();
bean.setCachingEnabled(false);
bean.setSessionDAO(sessionDAO);
bean.setCasOutHandler(casOutHandler);
bean.setCasServerUrl(Global.getProperty("shiro.casServerUrl"));
bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas");
@Order(3000)
@ConditionalOnMissingBean(name="shiroFilterProxy")
public FilterRegistrationBean shiroFilterProxy(ShiroFilterFactoryBean shiroFilter) throws Exception {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter((Filter) shiroFilter.getInstance());
bean.addUrlPatterns("/*");
return bean;
}
/**
* CAS登录过滤器
*/
private CasAuthenticationFilter shiroCasFilter(AuthorizingRealm authorizingRealm) {
private CasAuthenticationFilter shiroCasFilter(CasAuthorizingRealm casAuthorizingRealm) {
CasAuthenticationFilter bean = new CasAuthenticationFilter();
bean.setAuthorizingRealm(authorizingRealm);
bean.setAuthorizingRealm(casAuthorizingRealm);
return bean;
}
@@ -83,8 +82,10 @@ public class ShiroConfig {
/**
* 登出过滤器
*/
private LogoutFilter shiroLogoutFilter() {
return new LogoutFilter();
private LogoutFilter shiroLogoutFilter(AuthorizingRealm authorizingRealm) {
LogoutFilter bean = new LogoutFilter();
bean.setAuthorizingRealm(authorizingRealm);
return bean;
}
/**
@@ -113,15 +114,15 @@ public class ShiroConfig {
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(WebSecurityManager securityManager,
AuthorizingRealm authorizingRealm) {
AuthorizingRealm authorizingRealm, CasAuthorizingRealm casAuthorizingRealm) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setLoginUrl(Global.getProperty("shiro.loginUrl"));
bean.setSuccessUrl(Global.getProperty("shiro.successUrl"));
bean.setSuccessUrl(Global.getProperty("adminPath")+"/index");
Map<String, Filter> filters = bean.getFilters();
filters.put("cas", shiroCasFilter(authorizingRealm));
filters.put("cas", shiroCasFilter(casAuthorizingRealm));
filters.put("authc", shiroAuthcFilter(authorizingRealm));
filters.put("logout", shiroLogoutFilter());
filters.put("logout", shiroLogoutFilter(authorizingRealm));
filters.put("perms", shiroPermsFilter());
filters.put("roles", shiroRolesFilter());
filters.put("user", shiroUserFilter());
@@ -131,21 +132,57 @@ public class ShiroConfig {
bean.setFilterChainDefinitionMap(chains.getObject());
return bean;
}
/**
* 系统安全认证实现类
*/
@Bean
public AuthorizingRealm authorizingRealm(SessionDAO sessionDAO) {
AuthorizingRealm bean = new AuthorizingRealm();
bean.setSessionDAO(sessionDAO);
return bean;
}
/**
* 单点登录信息句柄,单点退出用
*/
@Bean
public CasOutHandler casOutHandler() {
return new CasOutHandler();
}
/**
* 系统安全认证实现类
*/
@Bean
public CasAuthorizingRealm casAuthorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) {
CasAuthorizingRealm bean = new CasAuthorizingRealm();
bean.setSessionDAO(sessionDAO);
bean.setCasOutHandler(casOutHandler);
bean.setCasServerUrl(Global.getProperty("shiro.casServerUrl"));
bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas");
return bean;
}
/**
* 定义Shiro安全管理配置
*/
@Bean
public WebSecurityManager securityManager(AuthorizingRealm authorizingRealm, SessionManager sessionManager, CacheManager shiroCacheManager) {
public WebSecurityManager securityManager(AuthorizingRealm authorizingRealm,
CasAuthorizingRealm casAuthorizingRealm, SessionManager sessionManager,
CacheManager shiroCacheManager) {
WebSecurityManager bean = new WebSecurityManager();
bean.setRealm(authorizingRealm);
Collection<Realm> realms = ListUtils.newArrayList();
realms.add(authorizingRealm); // 第一个为权限授权控制类
realms.add(casAuthorizingRealm);
bean.setRealms(realms);
bean.setSessionManager(sessionManager);
bean.setCacheManager(shiroCacheManager);
// 设置支持CAS的subjectFactory
bean.setSubjectFactory(new CasSubjectFactory());
return bean;
}
/**
* Shiro 生命周期处理器,实现初始化和销毁回调
*/

View File

@@ -3,19 +3,16 @@
*/
package com.jeesite.modules.config.web;
import javax.servlet.Filter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.CharacterEncodingFilter;
import com.jeesite.common.config.Global;
import com.jeesite.common.shiro.web.ShiroFilterFactoryBean;
import com.jeesite.common.web.PageCachingFilter;
/**
@@ -24,28 +21,15 @@ import com.jeesite.common.web.PageCachingFilter;
* @version 2017年11月30日
*/
@Configuration
public class FilterConfig {
/**
* Encoding Filter
*/
@Bean
@Order(1000)
public FilterRegistrationBean characterEncodingFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new CharacterEncodingFilter());
bean.addInitParameter("encoding", "UTF-8");
bean.addInitParameter("forceEncoding", "true");
bean.addUrlPatterns("/*");
return bean;
}
public class PageCacheConfig {
/**
* PageCache Filter, cache .html suffix.
*/
@Bean
@Order(2000)
@ConditionalOnProperty(name = "ehcache.pageCaching.enabled", havingValue = "true")
@ConditionalOnMissingBean(name="pageCachingFilter")
public FilterRegistrationBean pageCachingFilter(EhCacheManagerFactoryBean ehCacheManager) {
FilterRegistrationBean bean = new FilterRegistrationBean();
PageCachingFilter pageCachingFilter = new PageCachingFilter();
@@ -56,18 +40,5 @@ public class FilterConfig {
"ehcache.pageCaching.urlPatterns"), ","));
return bean;
}
/**
* Apache Shiro Filter
* @throws Exception
*/
@Bean
@Order(3000)
public FilterRegistrationBean shiroFilterProxy(ShiroFilterFactoryBean shiroFilter) throws Exception {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter((Filter) shiroFilter.getInstance());
bean.addUrlPatterns("/*");
return bean;
}
}

View File

@@ -12,6 +12,7 @@ import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.jeesite.common.lang.ByteUtils;
import com.jeesite.common.lang.DateUtils;
import com.jeesite.common.lang.TimeUtils;
import com.jeesite.common.service.BaseService;
@@ -21,12 +22,12 @@ import com.jeesite.modules.sys.utils.UserUtils;
/**
* 日志拦截器
* @author ThinkGem
* @version 2014-8-19
* @version 2018-08-11
*/
public class LogInterceptor extends BaseService implements HandlerInterceptor {
private static final ThreadLocal<Long> startTimeThreadLocal =
new NamedThreadLocal<Long>("ThreadLocal StartTime");
new NamedThreadLocal<Long>("LogInterceptor StartTime");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
@@ -61,10 +62,10 @@ public class LogInterceptor extends BaseService implements HandlerInterceptor {
// 打印JVM信息。
if (logger.isDebugEnabled()){
logger.debug("计时结束: {} 用时: {} URI: {} 最大内存: {}m 已分配内存: {}m 已分配内存中的剩余空间: {}m 最大可用内存: {}m",
DateUtils.formatDate(endTime, "hh:mm:ss.SSS"), TimeUtils.formatDateAgo(executeTime),
request.getRequestURI(), Runtime.getRuntime().maxMemory()/1024/1024, Runtime.getRuntime().totalMemory()/1024/1024, Runtime.getRuntime().freeMemory()/1024/1024,
(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory()+Runtime.getRuntime().freeMemory())/1024/1024);
Runtime runtime = Runtime.getRuntime();
logger.debug("计时结束: {} 用时: {} URI: {} 总内存: {} 已用内存: {}",
DateUtils.formatDate(endTime, "hh:mm:ss.SSS"), TimeUtils.formatDateAgo(executeTime), request.getRequestURI(),
ByteUtils.formatByteSize(runtime.totalMemory()), ByteUtils.formatByteSize(runtime.totalMemory()-runtime.freeMemory()));
}
}

View File

@@ -116,8 +116,7 @@ public class EmpUserService extends CrudService<EmpUserDao, EmpUser> {
int successNum = 0; int failureNum = 0;
StringBuilder successMsg = new StringBuilder();
StringBuilder failureMsg = new StringBuilder();
try {
ExcelImport ei = new ExcelImport(file, 2, 0);
try(ExcelImport ei = new ExcelImport(file, 2, 0)){
List<EmpUser> list = ei.getDataList(EmpUser.class);
for (EmpUser user : list) {
try{

View File

@@ -24,7 +24,6 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.shiro.filter.FormAuthenticationFilter;
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
@@ -87,7 +86,7 @@ public class LoginController extends BaseController{
}
// 是否显示验证码
model.addAttribute("isValidCodeLogin", ObjectUtils.toInteger(Global.getConfig("sys.login.failedNumAfterValidCode", "200")) == 0);
model.addAttribute("isValidCodeLogin", Global.getConfigToInteger("sys.login.failedNumAfterValidCode", "200") == 0);
//获取当前会话对象
Session session = UserUtils.getSession();
@@ -227,39 +226,52 @@ public class LoginController extends BaseController{
return REDIRECT + adminPath + "/login";
}
model.addAttribute("user", user); // 设置当前用户信息
// 登录成功后,验证码计算器清零
BaseAuthorizingRealm.isValidCodeLogin(loginInfo.getId(), /*loginInfo.getParam("corpCode"), */loginInfo.getParam("deviceType"), "success");
//获取当前会话对象
Session session = UserUtils.getSession();
// 设置共享SessionId的Cookie值睿思BI使用。
String cookieName = Global.getProperty("session.shareSessionIdCookieName");
if (StringUtils.isNotBlank(cookieName)){
CookieUtils.setCookie((HttpServletResponse)response, cookieName, (String)session.getId());
}
// 如果是登录操作,则设置登录信息(移动端用)
model.addAttribute("result", Global.TRUE);
if (request.getParameter("username") != null && request.getParameter("password") != null){
// 是否是登录操作
boolean isLogin = "true".equals(loginInfo.getParam("__login"));
if (isLogin){
// 获取后接着清除,防止下次获取仍然认为是登录状态
loginInfo.getParams().remove("__login");
// 设置共享SessionId的Cookie值第三方系统使用
String cookieName = Global.getProperty("session.shareSessionIdCookieName");
if (StringUtils.isNotBlank(cookieName)){
CookieUtils.setCookie((HttpServletResponse)response, cookieName, (String)session.getId());
}
// 如果登录设置了语言,则切换语言
if (loginInfo.getParam("lang") != null){
Global.setLang(loginInfo.getParam("lang"), request, response);
}
model.addAttribute("message", text("sys.login.success"));
}else{
model.addAttribute("message", text("sys.login.getInfo"));
}
model.addAttribute("sessionid", (String)session.getId());
// 获取登录成功页面
String successUrl = Global.getProperty("shiro.successUrl");
if (!StringUtils.contains(successUrl, "://")){
successUrl = request.getContextPath() + successUrl;
}
// 登录操作如果是Ajax操作直接返回登录信息字符串。
if (ServletUtils.isAjaxRequest(request)){
model.addAttribute("result", Global.TRUE);
// 如果是登录,则返回登录成功信息,否则返回获取成功信息
if (isLogin){
model.addAttribute("message", text("sys.login.success"));
}else{
model.addAttribute("message", text("sys.login.getInfo"));
}
model.addAttribute("sessionid", (String)session.getId());
model.addAttribute("__url", successUrl); // 告诉浏览器登录后跳转的页面
return ServletUtils.renderObject(response, model);
}
// 如果是登录操作,则跳转到登录成功页
else if (isLogin){
return REDIRECT + successUrl;
}
// 是否允许刷新主页,如果已登录,再次访问主页,则退出原账号。
if (!ObjectUtils.toBoolean(Global.getConfig("shiro.isAllowRefreshIndex", "true"))){
if (!Global.getConfigToBoolean("shiro.isAllowRefreshIndex", "true")){
String logined = CookieUtils.getCookie(request, "LOGINED");
if (StringUtils.isBlank(logined) || "false".equals(logined)){
CookieUtils.setCookie(response, "LOGINED", "true");

View File

@@ -18,10 +18,10 @@ import org.springframework.web.bind.annotation.ResponseBody;
import com.jeesite.common.collect.MapUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.msg.EmailUtils;
import com.jeesite.common.msg.SmsUtils;
import com.jeesite.common.service.ServiceException;
import com.jeesite.common.web.BaseController;
import com.jeesite.modules.msg.entity.MsgPush;
import com.jeesite.modules.msg.utils.MsgPushUtils;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.UserUtils;
@@ -49,21 +49,21 @@ public class AccountController extends BaseController{
}
/**
* 获取短信、邮件验证码
* 获取找回密码短信、邮件验证码
* @param validCode 图片验证码,防止重复机器人。
* @param validType 验证方式mobile、email
*/
@PostMapping(value = "getValidCode")
@PostMapping(value = "getFpValidCode")
@ResponseBody
public String getValidCode(User user, String validCode, String validType, HttpServletRequest request) {
public String getFpValidCode(User user, String validCode, String validType, HttpServletRequest request) {
// 校验图片验证码,防止重复机器人。
if (!ValidCodeUtils.validate(request, validCode)){
return renderResult(Global.FALSE, "验证码不正确或已失效!");
return renderResult(Global.FALSE, "图片验证码不正确或已失效,请点击图片刷新");
}
if (!"mobile".equals(validType) && !"email".equals(validType)){
return renderResult(Global.FALSE, "非法操作。");
}
User u = userService.getByLoginCode(user);
User u = UserUtils.getByLoginCode(user.getLoginCode());
if(u == null){
return renderResult(Global.FALSE, "登录账号不正确!");
}
@@ -106,17 +106,25 @@ public class AccountController extends BaseController{
String validCode = (String)UserUtils.getCache("fpValidCode");
Date date = (Date)UserUtils.getCache("fpLastDate");
// 一同验证保存的用户名和验证码是否正确(如果只校验验证码,不验证用户名,则会有获取验证码后修改用户名的漏洞)
if (!(userCode != null && loginCode != null && loginCode.equals(user.getLoginCode()))){
return renderResult(Global.FALSE, "请重新获取验证码!");
}
// 清理验证码,验证码只允许使用一次。
UserUtils.removeCache("fpUserCode");
UserUtils.removeCache("fpLoginCode");
UserUtils.removeCache("fpValidCode");
UserUtils.removeCache("fpLastDate");
// 验证码是否超时
boolean isTimeout = true;
String validTime = Global.getConfig("sys.account.validCodeTimeout", "10"); //验证码有效时间单位分钟0表示不限制默认值10
if("0".equals(validTime) || (date != null && (System.currentTimeMillis()-date.getTime())/(1000L) < 60*Long.parseLong(validTime))){
isTimeout = false;
}
// 一同验证保存的用户名和验证码是否正确(如果只校验验证码,不验证用户名,则会有获取验证码后修改用户名的漏洞)
if (!(userCode != null && loginCode != null && loginCode.equals(user.getLoginCode())
&& validCode != null && validCode.equals(fpValidCode) && !isTimeout)){
return renderResult(Global.FALSE, "验证码不正确或已失效!");
if (!(validCode != null && validCode.equals(fpValidCode) && !isTimeout)){
return renderResult(Global.FALSE, "验证码不正确或已失效,请重新获取验证码!");
}
// 更新为新密码。
@@ -125,12 +133,6 @@ public class AccountController extends BaseController{
}catch(ServiceException se){
return renderResult(Global.FALSE, se.getMessage());
}
// 修改密码成功后清理验证码,验证码只允许使用一次。
UserUtils.removeCache("fpUserCode");
UserUtils.removeCache("fpLoginCode");
UserUtils.removeCache("fpValidCode");
UserUtils.removeCache("fpLastDate");
return renderResult(Global.TRUE, "恭喜你,您的账号 "+loginCode+" 密码修改成功!");
}
@@ -143,13 +145,26 @@ public class AccountController extends BaseController{
public String getPwdQuestion(User user, String validCode, HttpServletRequest request) {
// 校验图片验证码,防止重复机器人。
if (!ValidCodeUtils.validate(request, validCode)){
return renderResult(Global.FALSE, "验证码不正确或已失效!");
return renderResult(Global.FALSE, "图片验证码不正确或已失效,请点击图片刷新");
}
// 账号是否存在验证
User u = userService.getByLoginCode(user);
User u = UserUtils.getByLoginCode(user.getLoginCode());
if (u == null){
return renderResult(Global.FALSE, "登录账号不正确!");
}
// 操作是否频繁验证, 如果离上次获取验证码小于20秒则提示操作频繁。
Date date = (Date)UserUtils.getCache("fpLastDate");
if (date != null && (System.currentTimeMillis()-date.getTime())/(1000L) < 20L){
return renderResult(Global.FALSE, "您当前操作太频繁,请稍等一会再操作!");
}else{
UserUtils.putCache("fpLastDate", new Date());
}
// 未设置密保
if (StringUtils.isAnyBlank(u.getPwdQuestion(), u.getPwdQuestion2(), u.getPwdQuestion3())){
return renderResult(Global.FALSE, "该账号未设置密保问题!");
}
// 获取保密问题,并缓存
Map<String, String> data = MapUtils.newHashMap();
data.put("pwdQuestion", u.getPwdQuestion());
@@ -171,9 +186,18 @@ public class AccountController extends BaseController{
public String savePwdByPwdQuestion(User user, HttpServletRequest request) {
String userCode = (String)UserUtils.getCache("fpUserCode");
String loginCode = (String)UserUtils.getCache("fpLoginCode");
User u = userService.getByLoginCode(user);
// 一同验证保存的用户名和验证码是否正确(如果只校验验证码,不验证用户名,则会有获取验证码后修改用户名的漏洞)
if (!(userCode != null && loginCode != null && loginCode.equals(user.getLoginCode()))){
return renderResult(Global.FALSE, "请重新获取保密问题!");
}
// 清理保密问题,每次获取只允许使用一次。
UserUtils.removeCache("fpUserCode");
UserUtils.removeCache("fpLoginCode");
// 验证三个密保问题是否正确。
User u = UserUtils.getByLoginCode(user.getLoginCode());
if (!(u != null && loginCode.equals(user.getLoginCode())
&& UserService.validatePassword(user.getPwdQuestionAnswer(), u.getPwdQuestionAnswer())
&& UserService.validatePassword(user.getPwdQuestionAnswer2(), u.getPwdQuestionAnswer2())
@@ -187,10 +211,6 @@ public class AccountController extends BaseController{
}catch(ServiceException se){
return renderResult(Global.FALSE, se.getMessage());
}
// 验证成功后清理缓存。
UserUtils.removeCache("fpUserCode");
UserUtils.removeCache("fpLoginCode");
return renderResult(Global.TRUE, "验证通过");
}
@@ -199,7 +219,6 @@ public class AccountController extends BaseController{
* @param user 用户信息参数
*/
@RequestMapping(value = "registerUser")
@ResponseBody
public String registerUser(User user, HttpServletRequest request) {
return "modules/sys/account/registerUser";
}
@@ -209,12 +228,12 @@ public class AccountController extends BaseController{
* @param user 用户信息参数
* @param validType 验证方式mobile、email
*/
@PostMapping(value = "getRegisterUserValidCode")
@PostMapping(value = "getRegValidCode")
@ResponseBody
public String getRegisterUserValidCode(User user, String validCode, String validType, HttpServletRequest request) {
public String getRegValidCode(User user, String validCode, String validType, HttpServletRequest request) {
// 校验图片验证码,防止重复机器人。
if (!ValidCodeUtils.validate(request, validCode)){
return renderResult(Global.FALSE, "验证码不正确或已失效!");
return renderResult(Global.FALSE, "图片验证码不正确或已失效,请点击图片刷新");
}
if (!"mobile".equals(validType) && !"email".equals(validType)){
return renderResult(Global.FALSE, "非法操作。");
@@ -238,7 +257,7 @@ public class AccountController extends BaseController{
UserUtils.putCache("regLastDate", new Date());
}
// 验证用户编码是否存在。
if (userService.getByLoginCode(user) != null){
if (UserUtils.getByLoginCode(user.getLoginCode()) != null){
return renderResult(Global.FALSE, "登录账号已存在!");
}
// 生成验证码,并缓存。
@@ -271,9 +290,9 @@ public class AccountController extends BaseController{
* @param user 用户信息参数
* @param validType 验证方式mobile、email
*/
@PostMapping(value = "saveRegisterUserByValidCode")
@PostMapping(value = "saveRegByValidCode")
@ResponseBody
public String saveRegisterUserByValidCode(User user, String regValidCode, HttpServletRequest request) {
public String saveRegByValidCode(User user, String regValidCode, HttpServletRequest request) {
if (!"true".equals(Global.getConfig("sys.account.registerUser"))){
return renderResult(Global.FALSE, "当前系统没有开启注册功能!");
}
@@ -285,6 +304,11 @@ public class AccountController extends BaseController{
String mobile = (String)UserUtils.getCache("regMobile");
String validCode = (String)UserUtils.getCache("regValidCode");
Date date = (Date)UserUtils.getCache("regLastDate");
// 一同验证保存的用户名和验证码是否正确(如果只校验验证码,不验证用户名,则会有获取验证码后修改用户名的漏洞)
if (!(loginCode != null && loginCode.equals(user.getLoginCode()))){
return renderResult(Global.FALSE, "非法操作。");
}
// 验证码是否超时
boolean isTimeout = true;
@@ -292,11 +316,8 @@ public class AccountController extends BaseController{
if("0".equals(validTime) || (date != null && (System.currentTimeMillis()-date.getTime())/(1000L) < 60*Long.parseLong(validTime))){
isTimeout = false;
}
// 一同验证保存的用户名和验证码是否正确(如果只校验验证码,不验证用户名,则会有获取验证码后修改用户名的漏洞)
if (!(loginCode != null && loginCode.equals(user.getLoginCode())
&& validCode != null && validCode.equals(regValidCode) && !isTimeout)){
return renderResult(Global.FALSE, "验证码不正确或已失效!");
if (!(validCode != null && validCode.equals(regValidCode) && !isTimeout)){
return renderResult(Global.FALSE, "验证码不正确或已失效,请重新获取验证码!");
}
// 非空数据校验。
@@ -333,33 +354,39 @@ public class AccountController extends BaseController{
* 发送邮件验证码
*/
private String sendEmailValidCode(User user, String code, String title){
String account = user.getEmail();
try {
title = user.getUserName() + "" + user.getLoginCode() + ""+title+"验证码";
String content = "尊敬的用户,您好!\n\n您的验证码是" + code +"(请勿透露给其他人)\n\n"
+ "请复制后,填写在你的验证码窗口完成验证。\n\n本邮件由系统自动发出请勿回复。\n\n感谢您的使用";
String receiveUserCode = "[CODE]"+user.getEmail();
MsgPushUtils.push(MsgPush.TYPE_EMAIL, title, content, null, null, receiveUserCode);
+ "请复制后,填写在你的验证码窗口完成验证。\n\n本邮件由系统自动发出请勿回复。\n\n感谢您的使用";
// String receiveUserCode = "[CODE]"+account;
// MsgPushUtils.push(MsgPush.TYPE_EMAIL, title, content, null, null, receiveUserCode);
EmailUtils.send(account, title, content);
} catch (Exception e) {
logger.error(title+"发送邮件错误。", e);
return renderResult(Global.FALSE, "系统出现了点问题,错误信息:" + e.getMessage());
}
return renderResult(Global.TRUE, "邮件已发送,请接收并填写验证码!");
account = account.replaceAll("([\\w\\W]?)([\\w\\W]+)([\\w\\W])(@[\\w\\W]+)", "$1****$3$4");
return renderResult(Global.TRUE, "验证码已发送到“"+account+"”邮箱账号,请尽快查收!");
}
/**
* 发送短信验证码
*/
private String sendSmsValidCode(User user, String code, String title){
String account = user.getMobile();
try {
title = user.getUserName() + "" + user.getLoginCode() + ""+title+"验证码";
String content = "您好,您的验证码是:" + code +"(请勿透露给其他人)感谢您的使用。";
String receiveUserCode = "[CODE]"+user.getMobile();
MsgPushUtils.push(MsgPush.TYPE_SMS, title, content, null, null, receiveUserCode);
// String receiveUserCode = "[CODE]"+account;
// MsgPushUtils.push(MsgPush.TYPE_SMS, title, content, null, null, receiveUserCode);
SmsUtils.send(content, account);
} catch (Exception e) {
logger.error(title+"发送短信错误。", e);
return renderResult(Global.FALSE, "系统出现了点问题,错误信息:" + e.getMessage());
}
return renderResult(Global.TRUE, "短信已发送,请接收并填写验证码!");
account = account.replaceAll("(\\d{3})(\\d+)(\\d{3})","$1****$3");
return renderResult(Global.TRUE, "验证码已发送到“"+account+"”的手机号码,请尽快查收!");
}
}

View File

@@ -22,12 +22,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.collect.MapUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.entity.Page;
import com.jeesite.common.lang.DateUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.mapper.JsonMapper;
import com.jeesite.common.utils.excel.ExcelExport;
import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
import com.jeesite.common.web.BaseController;
@@ -175,8 +177,9 @@ public class EmpUserController extends BaseController {
}
List<EmpUser> list = empUserService.findList(empUser);
String fileName = "用户数据" + DateUtils.getDate("yyyyMMddHHmmss") + ".xlsx";
new ExcelExport("用户数据", EmpUser.class).setDataList(list)
.write(response, fileName).dispose();
try(ExcelExport ee = new ExcelExport("用户数据", EmpUser.class)){
ee.setDataList(list).write(response, fileName);
}
}
/**
@@ -194,8 +197,9 @@ public class EmpUserController extends BaseController {
}
List<EmpUser> list = ListUtils.newArrayList(empUser);
String fileName = "用户数据模板.xlsx";
new ExcelExport("用户数据", EmpUser.class, Type.IMPORT).setDataList(list)
.write(response, fileName).dispose();
try(ExcelExport ee = new ExcelExport("用户数据", EmpUser.class, Type.IMPORT)){
ee.setDataList(list).write(response, fileName);
}
}
/**
@@ -326,6 +330,7 @@ public class EmpUserController extends BaseController {
if (!EmpUser.USER_TYPE_EMPLOYEE.equals(empUser.getUserType())){
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
empUser.setMgrType(User.MGR_TYPE_NOT_ADMIN);
userService.saveAuthDataScope(empUser);
return renderResult(Global.TRUE, text("用户分配数据权限成功"));
}
@@ -363,9 +368,9 @@ public class EmpUserController extends BaseController {
if (!(isAll != null && isAll)) {
empUserService.addDataScopeFilter(empUser);
}
List<User> list = userService.findList(empUser);
List<EmpUser> list = empUserService.findList(empUser);
for (int i = 0; i < list.size(); i++) {
User e = list.get(i);
EmpUser e = list.get(i);
Map<String, Object> map = MapUtils.newHashMap();
map.put("id", StringUtils.defaultIfBlank(idPrefix, "u_") + e.getId());
map.put("pId", StringUtils.defaultIfBlank(pId, "0"));
@@ -375,4 +380,19 @@ public class EmpUserController extends BaseController {
return mapList;
}
/**
* 选择员工对话框
*/
@RequiresPermissions("user")
@RequestMapping(value = "empUserSelect")
public String empUserSelect(EmpUser empUser, String selectData, String checkbox, Model model) {
String selectDataJson = EncodeUtils.decodeUrl(selectData);
if (JsonMapper.fromJson(selectDataJson, Map.class) != null){
model.addAttribute("selectData", selectDataJson);
}
model.addAttribute("checkbox", checkbox); // 是否显示复选框,支持多选
model.addAttribute("empUser", empUser); // ModelAttribute
return "modules/sys/user/empUserSelect";
}
}

View File

@@ -84,7 +84,7 @@ public class SecAdminController extends BaseController {
if (!User.USER_TYPE_EMPLOYEE.equals(user.getUserType())){
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
// 设置为二级管理员身份(必须先设置二级管理员身份,再保存管理数据权限,否则无法设置管理数据权限数据)
// 设置为二级管理员身份
user.setMgrType(User.MGR_TYPE_SEC_ADMIN);
userService.updateMgrType(user);
// 保存用户管理数据权限
@@ -107,7 +107,8 @@ public class SecAdminController extends BaseController {
if (!User.USER_TYPE_EMPLOYEE.equals(user.getUserType())){
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
// 取消用户管理数据权限(必须先删除管理权限,再取消二级管理员身份,否则无法清理管理数据权限数据)
// 取消用户管理数据权限
user.setMgrType(User.MGR_TYPE_SEC_ADMIN);
user.setUserDataScopeListJson("[]");
userService.saveAuthDataScope(user);
// 取消二级管理员身份

View File

@@ -25,6 +25,23 @@ jdbc:
# 最大连接数
maxActive: 20
# 获取连接等待超时时间单位毫秒4.0.6+
maxWait: 60000
# 从池中取出连接前进行检验如果检验失败则从池中去除连接并尝试取出另一个。4.0.6+
testOnBorrow: false
testOnReturn: false
# 间隔多久才进行一次检测检测需要关闭的空闲连接单位毫秒4.0.6+
timeBetweenEvictionRunsMillis: 60000
# 一个连接在池中最小生存的时间单位毫秒4.0.6+
minEvictableIdleTimeMillis: 300000
# 配置是否自动回收超时连接,超时时间,单位秒 4.0.6+
removeAbandoned: true
removeAbandonedTimeout: 1800
# # 多数据源名称列表,启用方式:@MyBatisDao(dataSourceName="ds2")
# dataSourceNames: ds2
#
@@ -56,8 +73,9 @@ jdbc:
stat:
enabled: true
# Redis 配置
# Redis 配置2.x/3.x
redis:
enabled: false
# Redis 连接参数
host: 127.0.0.1
@@ -155,6 +173,13 @@ user:
# 多租户模式SAAS模式专业版
useCorpModel: false
# 自助账号服务
account:
# 注册用户
registerUser:
enabled: false
userTypes: 0, 1
# 任务调度(个人版+
job:
@@ -172,6 +197,7 @@ job:
# 任务调度集群设置
jobStore:
isClustered: true
dataSourceName: job
clusterCheckinInterval: 1000
# 内容管理
@@ -195,14 +221,14 @@ shiro:
logoutUrl: ${shiro.loginUrl}
successUrl: ${adminPath}/index
# CAS 相关配置
# casServerUrl: http://192.168.1.3:8080/cas
# casClientUrl: http://192.168.1.3:8180/jeesite
# # Jasig CAS 相关配置
# casServerUrl: http://127.0.0.1:8981/cas
# casClientUrl: http://127.0.0.1:8980/js
# loginUrl: ${shiro.casServerUrl}?service=${shiro.casClientUrl}${adminPath}/login-cas
# logoutUrl: ${shiro.casServerUrl}/logout?service=${shiro.loginUrl}
# successUrl: ${shiro.casClientUrl}${adminPath}/index
# SSO 登录相关配置
# 简单 SSO 登录相关配置
sso:
# 如果启用/sso/{username}/{token}单点登录请修改此安全key并与单点登录系统key一致。
@@ -211,12 +237,15 @@ shiro:
# 是否加密单点登录安全Key
encryptKey: true
# 登录提交信息加密
# 登录提交信息加密(如果不需要加密,设置为空即可)
loginSubmit:
# 登录提交信息安全Key加密用户名、密码、验证码后再提交key设置为3个用逗号分隔
secretKey: thinkgem,jeesite,com
# 指定获取客户端IP的Header名称防止IP伪造。指定为空则使用原生方法获取IP。
remoteAddrHeaderName: X-Forwarded-For
# 允许的请求方法设定,解决安全审计问题
allowRequestMethods: GET,POST
@@ -233,6 +262,9 @@ shiro:
# accessControlAllowOrigin: http://demo.jeesite.net
# accessControlAllowOrigin: '*'
# 是否在登录后生成新的Session默认false
isGenerateNewSessionAfterLogin: false
# URI 权限过滤器定义
filterChainDefinitions: |
/ReportServer/** = user
@@ -338,6 +370,7 @@ web:
${adminPath}/login,
${adminPath}/desktop,
${adminPath}/sys/online/count,
${adminPath}/state/server/rtInfo,
${adminPath}/**/treeData,
${adminPath}/file/**,
${adminPath}/tags/*,
@@ -360,7 +393,7 @@ web:
validator:
id: '[a-zA-Z0-9_\-/\u4e00-\u9fa5]{0,64}'
user.loginCode: '[a-zA-Z0-9_\u4e00-\u9fa5]{4,20}'
# 错误页面500.html是否输出错误信息正式环境为提供安全性可设置为false
error:
page:
@@ -421,10 +454,10 @@ msg:
# 短信网关
sms:
beanName: smsSendService
url: http://localhost:80/msg/sendSms
data: account=demo&pswd=demo&product=
prefix: ~
suffix: 【JeeSite】
url: http://lehuo520.cn/a/sms/api
data: username=jeesite&password=jeesite.com
prefix: 【JeeSite】
suffix: ~
# 微信相关
weixin:

View File

@@ -0,0 +1,6 @@
UPDATE ${_prefix}sys_menu SET permission='sys:stste:cache' WHERE permission='sys:stste:ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/cache/index' WHERE menu_href='/state/ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/server/index' WHERE menu_href='/state/server';
commit;

View File

@@ -0,0 +1,6 @@
UPDATE ${_prefix}sys_menu SET permission='sys:stste:cache' WHERE permission='sys:stste:ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/cache/index' WHERE menu_href='/state/ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/server/index' WHERE menu_href='/state/server';
commit;

View File

@@ -0,0 +1,6 @@
UPDATE ${_prefix}sys_menu SET permission='sys:stste:cache' WHERE permission='sys:stste:ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/cache/index' WHERE menu_href='/state/ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/server/index' WHERE menu_href='/state/server';
commit;

View File

@@ -0,0 +1,6 @@
UPDATE ${_prefix}sys_menu SET permission='sys:stste:cache' WHERE permission='sys:stste:ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/cache/index' WHERE menu_href='/state/ehcache';
UPDATE ${_prefix}sys_menu SET menu_href='/state/server/index' WHERE menu_href='/state/server';
commit;

View File

@@ -2,4 +2,6 @@
4.0.1
4.0.2
4.0.3
4.0.4
4.0.4
4.0.5
4.0.6

View File

@@ -10,7 +10,6 @@ sys.logout.success=Logout successful!
# =========== 账号登录相关 ===========
sys.login.typeUnknown=Unknown login type.
sys.login.accountIsBlank=Login account cannot be empty.
sys.login.validCodeError=Login verification code error.
sys.login.accountDisabled=This Account has disabled.

View File

@@ -10,7 +10,6 @@ sys.logout.success=退出成功!
# =========== 账号登录相关 ===========
sys.login.typeUnknown=未知的登录类型。
sys.login.accountIsBlank=登录账号不能为空。
sys.login.validCodeError=登录验证码错误,请重试。
sys.login.accountDisabled=该帐号已停用。

View File

@@ -58,8 +58,8 @@
正在验证登录,请稍后...=Verify login, please later...
立即登录=Login
注册账号=Registered account
忘记密码=Forgot password
注册账号=Register
忘记密码=Forget Password
# =========== 主框架页 ===========

View File

@@ -49,8 +49,8 @@ else {
class="fa fa-reply-all"></i> ${text('sys.error.returnButton')}</button>
</div>
<div class="copyright">
<% var productName = @Global.getConfig('productName'), productVersion = @Global.getConfig('productVersion'); %>
&copy; ${@DateUtils.getYear()} ${productName} - Powered By <a href="http://jeesite.com">JeeSite</a>.
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a
href="http://jeesite.com">JeeSite ${@Global.getProperty('jeesiteVersion')}</a>
</div>
</div>
<% } %>

View File

@@ -6,8 +6,11 @@ var message = @ObjectUtils.toString(@request.getAttribute("message"));
if (isBlank(message)){
var ex = @ExceptionUtils.getThrowable(request);
if (ex != null){
if(@StringUtils.startsWith(@ex.getMessage(), "msg:")){
message = @StringUtils.replace(@ex.getMessage(), "msg:", "");
for (e in [ex, ex.cause!, ex.cause.cause!]){
if (@StringUtils.startsWith(e.message!, "msg:")){
message = @StringUtils.replace(e.message!, "msg:", "");
break;
}
}
}
}
@@ -35,8 +38,8 @@ else {
class="fa fa-reply-all"></i> ${text('sys.error.returnButton')}</button>
</div>
<div class="copyright">
<% var productName = @Global.getConfig('productName'), productVersion = @Global.getConfig('productVersion'); %>
&copy; ${@DateUtils.getYear()} ${productName} - Powered By <a href="http://jeesite.com">JeeSite</a>.
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a
href="http://jeesite.com">JeeSite ${@Global.getProperty('jeesiteVersion')}</a>
</div>
</div>
<% } %>

View File

@@ -37,8 +37,8 @@ else {
class="fa fa-reply-all"></i> ${text('sys.error.returnButton')}</button>
</div>
<div class="copyright">
<% var productName = @Global.getConfig('productName'), productVersion = @Global.getConfig('productVersion'); %>
&copy; ${@DateUtils.getYear()} ${productName} - Powered By <a href="http://jeesite.com">JeeSite</a>.
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a
href="http://jeesite.com">JeeSite ${@Global.getProperty('jeesiteVersion')}</a>
</div>
</div>
<% } %>

View File

@@ -7,14 +7,14 @@ var ex;
if (isBlank(message)){
ex = @ExceptionUtils.getThrowable(request);
if (ex != null){
if (@StringUtils.startsWith(ex.message, "msg:")){
message = @StringUtils.replace(ex.message, "msg:", "");
}else if (@StringUtils.startsWith(ex.cause.message, "msg:")){
message = @StringUtils.replace(ex.cause.message, "msg:", "");
}else if (ex.class.name == 'com.jeesite.common.service.ServiceException'){
message = ex.message;
}else if (ex.cause.class.name == 'com.jeesite.common.service.ServiceException'){
message = ex.cause.message;
for (e in [ex, ex.cause!, ex.cause.cause!]){
if (@StringUtils.startsWith(e.message!, "msg:")){
message = @StringUtils.replace(e.message!, "msg:", "");
break;
}else if (e.class.name! == 'com.jeesite.common.service.ServiceException'){
message = e.message!;
break;
}
}
@org.slf4j.LoggerFactory.getLogger("error/500").error(ex.message, ex);
}
@@ -43,11 +43,11 @@ else {
class="fa fa-reply-all"></i> ${text('sys.error.returnButton')}</button>
</div>
<div class="copyright">
<% var productName = @Global.getConfig('productName'), productVersion = @Global.getConfig('productVersion'); %>
&copy; ${@DateUtils.getYear()} ${productName} - Powered By <a href="http://jeesite.com">JeeSite</a>.
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a
href="http://jeesite.com">JeeSite ${@Global.getProperty('jeesiteVersion')}</a>
</div>
</div>
<% if (@ObjectUtils.toBoolean(@Global.getConfig('error.page.printErrorInfo', 'true'))){ %>
<% if (@Global.getConfigToBoolean('error.page.printErrorInfo', 'true')){ %>
<div class="box mt20">
${@StringUtils.toHtml(@ExceptionUtils.getStackTraceAsString(ex))}<br/>
此异常信息若不想输出可打开jeesite.yml文件设置error.page.printErrorInfo=false即可

View File

@@ -1,18 +1,10 @@
<%/* Copyright (c) 2013-Now http://jeesite.com All rights reserved. */
var productName = @Global.getConfig('productName') + ' ' + @Global.getConfig('productVersion');
var pageTitle = (isNotBlank(title!) ? title! + ' - ' : '') + productName;
var globalFields = @Global.getConst('Global.Fields');
%>
<meta charset="utf-8">
<meta content="webkit" name="renderer"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
<meta http-equiv="Pragma" content="no-cache"/><meta http-equiv="Expires" content="0"/>
<meta content="width=device-width, initial-scale=1, user-scalable=1" name="viewport"/>
<meta content="${productName} - Powered By JeeSite V4.0" name="description"/>
<meta content="ThinkGem, http://jeesite.com" name="author"/>
<meta content="Powered By JeeSite V4.0" name="keywords"/>
<title>${pageTitle}</title>
<%/* Copyright (c) 2013-Now http://jeesite.com All rights reserved. */%>
<meta charset="utf-8"><meta content="webkit" name="renderer"/><meta http-equiv="X-UA-Compatible"
content="IE=edge"><meta name="keywords" content="PoweredByJeeSiteV4.0"/><meta http-equiv="Cache-Control"
content="no-cache, no-store, must-revalidate"/><meta name="description" content="PoweredByJeeSiteV4.0"/><meta
content="no-cache" http-equiv="Pragma"/><meta http-equiv="Expires" content="0"/><meta
content="width=device-width, initial-scale=1, user-scalable=1" name="viewport"/>
<title>${(isNotBlank(title!) ? title! + ' - ' : '') + @Global.getConfig('productName')}</title>
<script src="${ctxPath}/global.min.js?ctx=${ctx}"></script>
<script src="${ctxStatic}/jquery/jquery-1.12.4.min.js"></script>
<script src="${ctxStatic}/jquery/jquery-migrate-1.4.1.min.js"></script>

View File

@@ -0,0 +1,188 @@
<% layout('/layouts/default.html', {title: '忘记密码', libs: ['validate'], bodyClass: 'login-page'}){ %>
<% include('/include/upgrade.html'){} // 如果客户浏览器版本过低,则显示浏览器升级提示。 %>
<link rel="stylesheet" href="${ctxStatic}/icheck/1.0/square/blue.css?${_version}">
<link rel="stylesheet" href="${ctxStatic}/jquery-toastr/2.0/toastr.min.css?${_version}">
<link rel="stylesheet" href="${ctxStatic}/modules/sys/sysLogin.css?${_version}">
<div class="login-box" style="margin-top:4%">
<div class="login-logo">
<a href="${ctxPath}/account/forgetPwd"><b>${@Global.getConfig('productName')}</b>
<small>${@Global.getConfig('productVersion')}</small></a>
</div>
<div class="login-box-body">
<form id="forgetForm" action="${ctxPath}/account/forgetPwd" method="post">
<div class="form-group has-feedback">
<select id="fp_validType" name="op" class="form-control">
<option value="mobile">使用手机号码找回您的密码</option>
<option value="email">使用电子邮箱找回您的密码</option>
<option value="question">使用保密问题找回您的密码</option>
</select>
<script type="text/javascript">
$(document).ready(function(){
$('#fp_validType').change(function(){
var val = $(this).val(), action = '';
$('.fp-element').addClass('hide').removeClass('block');
$('.fp-'+val).addClass('block').removeClass('hide');
setTimeout(function(){
$('#fp_loginCode').focus();
}, 100);
if (val == 'mobile' || val == 'email'){
var txt = (val == 'mobile' ? '手机' : '邮箱')
$('#fpValidCode').attr('placeholder', txt+'验证码')
.attr('data-msg-required', '请填写'+txt+'验证码.');
$('#sendFpValidCode').val('获取'+txt+'验证码');
action = '${ctxPath}/account/savePwdByValidCode';
}else if(val == 'question'){
action = '${ctxPath}/account/savePwdByPwdQuestion';
}
$('#forgetForm').attr('action', action);
}).change();
});
</script>
</div>
<div class="form-group has-feedback">
<span class="fa fa-user form-control-feedback"></span>
<input type="text" id="fp_loginCode" name="loginCode" class="form-control required" data-msg-required="请填写登录账号." placeholder="登录账号" />
</div>
<div class="form-group has-feedback fp-element fp-mobile fp-email fp-question">
<#form:validcode id="fp_validCode" name="validCode" isRequired="true" isRemote="true" isLazy="false"/>
</div>
<div class="form-group has-feedback fp-element fp-mobile fp-email">
<div class="input-group">
<input type="text" id="fpValidCode" name="fpValidCode" class="form-control required"
data-msg-required="请填写手机验证码." placeholder="手机验证码" />
<span class="input-group-btn">
<input type="button" id="sendFpValidCode" value="获取手机验证码" class="btn btn-flat"/>
</span>
</div>
<script type="text/javascript">
var waitTime = 60;
function sendTime(o) {
if (waitTime == 0) {
o.removeAttribute("disabled");
o.value = "获取验证码";
waitTime = 60;
} else {
o.setAttribute("disabled", true);
o.value = "重新发送(" + waitTime + ")";
waitTime--;
setTimeout(function() {
sendTime(o)
}, 1000);
}
}
$('#sendFpValidCode').click(function() {
var $this = this;
js.ajaxSubmit('${ctxPath}/account/getFpValidCode', {
validType: $('#fp_validType').val(),
loginCode : $('#fp_loginCode').val(),
validCode : $('#fp_validCode').val()
}, function(data){
js.showMessage(data.message);
if (data.result == 'true'){
sendTime($this);
}
});
});
</script>
</div>
<div class="form-group has-feedback fp-element fp-question clearfix">
<input type="button" id="fp_getQuestion" value="获取保密问题" class="btn btn-default btn-block btn-flat"/>
<script type="text/javascript">
$('#fp_getQuestion').click(function() {
js.ajaxSubmit('${ctxPath}/account/getPwdQuestion', {
loginCode : $('#fp_loginCode').val(),
validCode : $('#fp_validCode').val()
}, function(data){
js.showMessage(data.message);
if (data.result == 'true'){
$('#fp_q1').text(data.pwdQuestion);
$('#fp_q2').text(data.pwdQuestion2);
$('#fp_q3').text(data.pwdQuestion3);
}
});
});
</script>
</div>
<div class="form-group has-feedback fp-element fp-question">
问题1<span id="fp_q1"></span>
</div>
<div class="form-group has-feedback fp-element fp-question">
<span class="fa fa-question-circle form-control-feedback"></span>
<input type="text" name="pwdQuestionAnswer" class="form-control required"
data-msg-required="请填写答案1." placeholder="答案1 " />
</div>
<div class="form-group has-feedback fp-element fp-question">
问题2<span id="fp_q2"></span>
</div>
<div class="form-group has-feedback fp-element fp-question">
<span class="fa fa-question-circle form-control-feedback"></span>
<input type="text" name="pwdQuestionAnswer2" class="form-control required"
data-msg-required="请填写答案2." placeholder="答案2" />
</div>
<div class="form-group has-feedback fp-element fp-question">
问题3<span id="fp_q3"></span>
</div>
<div class="form-group has-feedback fp-element fp-question">
<span class="fa fa-question-circle form-control-feedback"></span>
<input type="text" name="pwdQuestionAnswer3" class="form-control required"
data-msg-required="请填写答案3." placeholder="答案3" />
</div>
<div class="form-group has-feedback clearfix">
<strong>设置新密码:</strong>
</div>
<div class="form-group has-feedback">
<span class="fa fa-lock form-control-feedback"></span>
<input type="password" autocomplete="off" id="fp_password" name="password"
class="form-control required" data-msg-required="请填写新密码."
rangelength="3,50" data-msg-rangelength="新密码长度不能小于3并大于50个字符."
placeholder="新密码" />
</div>
<div class="form-group has-feedback">
<span class="fa fa-lock form-control-feedback"></span>
<input type="password" autocomplete="off" id="fp_confirmPassword" name="confirmPassword"
class="form-control required" data-msg-required="请填写确认新密码."
rangelength="3,50" data-msg-rangelength="新密码长度不能小于3并大于50个字符."
equalTo="#fp_password" data-msg-equalTo="新密码与确认新密码不同."
placeholder="确认新密码" />
</div>
<div class="row">
<div class="col-xs-6">
<button type="submit" class="btn btn-primary btn-block btn-flat"
id="btnSubmit">${text('提交')}</button>
</div>
<div class="col-xs-6">
<button type="button" class="btn btn-default btn-block btn-flat"
id="btnReset">${text('返回')}</button>
</div>
</div>
<div class="clearfix"></div>
</form>
</div>
<div class="login-copyright">
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a
href="http://jeesite.com">JeeSite ${@Global.getProperty('jeesiteVersion')}</a>
</div>
</div>
<% } %>
<script>var secretKey = '${@Global.getConfig("shiro.loginSubmit.secretKey")}';</script>
<script src="${ctxStatic}/jquery-toastr/2.0/toastr.min.js?${_version}"></script>
<script src="${ctxStatic}/common/des.js?${_version}"></script>
<script>
$('#forgetForm').validate({
ignore: ":hidden",
submitHandler: function(form) {
js.ajaxSubmitForm($(form), function(data){
if (data.result == "true"){
alert(data.message);
location = "${ctx}/login";
}else{
js.showMessage(data.message);
$('#forgetForm').reset();
}
});
}
});
$('#btnReset').click(function(){
location = '${ctx}/login';
});
</script>

View File

@@ -10,11 +10,8 @@
<span class="info-box-text">CPU 使用率</span>
<span class="info-box-number">90<small>%</small></span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-red"><i class="fa fa-google-plus"></i></span>
@@ -22,11 +19,8 @@
<span class="info-box-text">关注数</span>
<span class="info-box-number">41,410</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<!-- fix for small devices only -->
<div class="clearfix visible-sm-block"></div>
@@ -38,11 +32,8 @@
<span class="info-box-text">因特网</span>
<span class="info-box-number">760</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
<div class="col-md-3 col-sm-6 col-xs-12">
<div class="info-box">
<span class="info-box-icon bg-yellow"><i class="fa fa-users"></i></span>
@@ -51,16 +42,13 @@
<span class="info-box-text">新用户</span>
<span class="info-box-number">2,000</span>
</div>
<!-- /.info-box-content -->
</div>
<!-- /.info-box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
<!-- Chart boxes -->
<div class="row">
<div class="col-md-12">
<div class="box">
<div class="box box-widget">
<div class="box-header with-border">
<h3 class="box-title">月度报告</h3>
<div class="box-tools pull-right">
@@ -82,7 +70,6 @@
</button>
</div>
</div>
<!-- /.box-header -->
<div class="box-body">
<div class="row">
<div class="col-md-8">
@@ -157,9 +144,7 @@
myChart.setOption(option);
</script>
</div>
<!-- /.chart-responsive -->
</div>
<!-- /.col -->
<div class="col-md-4">
<p class="text-center">
<strong>完成目标</strong>
@@ -171,7 +156,6 @@
<div class="progress-bar progress-bar-aqua" style="width: 80%"></div>
</div>
</div>
<!-- /.progress-group -->
<div class="progress-group">
<span class="progress-text">完成购买率</span>
<span class="progress-number"><b>310</b>/400</span>
@@ -179,7 +163,6 @@
<div class="progress-bar progress-bar-red" style="width: 80%"></div>
</div>
</div>
<!-- /.progress-group -->
<div class="progress-group">
<span class="progress-text">产品访问量</span>
<span class="progress-number"><b>480</b>/800</span>
@@ -187,7 +170,6 @@
<div class="progress-bar progress-bar-green" style="width: 80%"></div>
</div>
</div>
<!-- /.progress-group -->
<div class="progress-group">
<span class="progress-text">查询访问量</span>
<span class="progress-number"><b>250</b>/500</span>
@@ -195,13 +177,9 @@
<div class="progress-bar progress-bar-yellow" style="width: 80%"></div>
</div>
</div>
<!-- /.progress-group -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<!-- ./box-body -->
<div class="box-footer">
<div class="row">
<div class="col-sm-3 col-xs-6">
@@ -210,50 +188,37 @@
<h5 class="description-header">¥35,210.43</h5>
<span class="description-text">总营收</span>
</div>
<!-- /.description-block -->
</div>
<!-- /.col -->
<div class="col-sm-3 col-xs-6">
<div class="description-block border-right">
<span class="description-percentage text-yellow"><i class="fa fa-caret-left"></i> 0%</span>
<h5 class="description-header">¥10,390.90</h5>
<span class="description-text">总成本</span>
</div>
<!-- /.description-block -->
</div>
<!-- /.col -->
<div class="col-sm-3 col-xs-6">
<div class="description-block border-right">
<span class="description-percentage text-green"><i class="fa fa-caret-up"></i> 20%</span>
<h5 class="description-header">¥24,813.53</h5>
<span class="description-text">总利润</span>
</div>
<!-- /.description-block -->
</div>
<!-- /.col -->
<div class="col-sm-3 col-xs-6">
<div class="description-block">
<span class="description-percentage text-red"><i class="fa fa-caret-down"></i> 18%</span>
<h5 class="description-header">1200</h5>
<span class="description-text">目标完成</span>
</div>
<!-- /.description-block -->
</div>
</div>
<!-- /.row -->
</div>
<!-- /.box-footer -->
</div>
<!-- /.box -->
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</div>
<footer class="main-footer m0">
<% var productName = @Global.getConfig('productName'), productVersion = @Global.getConfig('productVersion'); %>
<div class="pull-right hidden-xs">当前版本: ${productVersion}</div>
&copy; ${@DateUtils.getYear()} ${productName} - Powered By <a href="http://jeesite.com">JeeSite</a>.
<div class="pull-right hidden-xs">当前版本: ${@Global.getConfig('productVersion')}</div>
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a href="http://jeesite.com">JeeSite</a>
</footer>
<% } %>
<script>

View File

@@ -15,7 +15,7 @@
<#form:hidden path="userCode"/>
<div class="box-body">
<div class="form-unit">基本信息</div>
<div class="row ${@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))?'':'hide'}">
<div class="row ${@Global.getConfigToBoolean('user.useCorpModel', 'false')?'':'hide'}">
<div class="col-xs-6">
<div class="form-group">
<label class="col-sm-4 control-label" title="">

View File

@@ -8,7 +8,7 @@
<div class="box-tools pull-right">
<a href="#" class="btn btn-default" id="btnSearch" title="查询"><i class="fa fa-filter"></i> 查询</a>
<% if(hasPermi('sys:corpAdmin:edit')){ %>
<% if(@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))){ %>
<% if(@Global.getConfigToBoolean('user.useCorpModel', 'false')){ %>
<a href="${ctx}/sys/corpAdmin/form?op=addCorp" class="btn btn-default btnTool" title="新增租户管理员"><i class="fa fa-plus"></i> 新增租户管理员</a>
<% }else{ %>
<a href="${ctx}/sys/corpAdmin/form?corpCode_=${user.currentUser.corpCode_}&corpName_=${user.currentUser.corpName_}&op=addAdmin" class="btn btn-default btnTool" title="新增管理员"><i class="fa fa-plus"></i> 新增管理员</a>
@@ -31,13 +31,13 @@
<#form:input path="userName" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-group ${@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))?'':'hide'}">
<div class="form-group ${@Global.getConfigToBoolean('user.useCorpModel', 'false')?'':'hide'}">
<label class="control-label">租户代码:</label>
<div class="control-inline">
<#form:input path="corpCode_" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-group ${@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))?'':'hide'}">
<div class="form-group ${@Global.getConfigToBoolean('user.useCorpModel', 'false')?'':'hide'}">
<label class="control-label">租户名称:</label>
<div class="control-inline">
<#form:input path="corpName_" maxlength="100" class="form-control width-90"/>
@@ -69,7 +69,7 @@ $('#dataGrid').dataGrid({
return '<a href="${ctx}/sys/corpAdmin/form?userCode='+row.userCode+'&op=edit" class="btnList" data-title="编辑用户">'+(val||row.id)+'</a>';
}},
{header:'用户昵称', name:'userName', index:'a.user_name', width:200, align:"center"},
<% if(@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))){ %>
<% if(@Global.getConfigToBoolean('user.useCorpModel', 'false')){ %>
{header:'租户代码', name:'corpCode_', index:'a.corp_code', width:200, align:"center", formatter: function(val, obj, row, act){
return '<a href="javascript:" onclick="$(\'#corpCode_\').val(\''+val+'\');$(\'#searchForm\').submit()">'+val+'</a>';
}},
@@ -93,7 +93,7 @@ $('#dataGrid').dataGrid({
actions.push('<a href="${ctx}/sys/corpAdmin/enable?userCode='+row.userCode+'" class="btnList" title="启用用户" data-confirm="确认要启用该用户吗?"><i class="glyphicon glyphicon-ok-circle"></i></a>&nbsp;');
}
actions.push('<a href="${ctx}/sys/corpAdmin/delete?userCode='+row.userCode+'" class="btnList" title="删除用户" data-confirm="确认要删除该用户吗?"><i class="fa fa-trash-o"></i></a>&nbsp;');
<% if(@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))){ %>
<% if(@Global.getConfigToBoolean('user.useCorpModel', 'false')){ %>
actions.push('<a href="${ctx}/sys/corpAdmin/form?corpCode_='+row.corpCode_+'&corpName_='+row.corpName_+'&op=addAdmin" class="btnList" title="新增管理员"><i class="fa fa-plus-square"></i></a>&nbsp;');
<% } %>
actions.push('<a href="javascript:" class="btnMore" title="更多操作"><i class="fa fa-chevron-circle-right"></i></a>&nbsp;');

View File

@@ -0,0 +1,166 @@
<% layout('/layouts/default.html', {title: '用户选择', libs: ['dataGrid']}){ %>
<div class="main-content">
<div class="box box-main">
<div class="box-body">
<#form:form id="searchForm" action="${ctx}/sys/empUser/listData" method="post" class="form-inline "
data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
<#form:hidden name="status" value="${isNotBlank(empUser.status) ? empUser.status : '0'}"/>
<div class="form-group">
<label class="control-label">${text('账号')}</label>
<div class="control-inline">
<#form:input path="loginCode" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('昵称')}</label>
<div class="control-inline">
<#form:input path="userName" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('邮箱')}</label>
<div class="control-inline">
<#form:input path="email" maxlength="300" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('手机')}</label>
<div class="control-inline">
<#form:input path="mobile" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-row"></div>
<div class="form-group">
<label class="control-label">${text('姓名')}</label>
<div class="control-inline">
<#form:input path="refName" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('机构')}</label>
<div class="control-inline width-90">
<#form:treeselect id="office" title="${text('机构选择')}"
path="employee.office.officeCode" labelPath="employee.office.officeName"
url="${ctx}/sys/office/treeData" btnClass="btn-sm" allowClear="true" canSelectParent="true"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('公司')}</label>
<div class="control-inline width-90">
<#form:treeselect id="company" title="${text('公司选择')}"
path="employee.company.companyCode" labelPath="employee.company.companyName"
url="${ctx}/sys/company/treeData" btnClass="btn-sm" allowClear="true" canSelectParent="true"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('电话')}</label>
<div class="control-inline">
<#form:input path="phone" maxlength="100" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">查询</button>
<button type="reset" class="btn btn-default btn-sm">重置</button>
</div>
</#form:form>
<div class="row">
<div class="col-xs-10 pr10">
<table id="dataGrid"></table>
<div id="dataGridPage"></div>
</div>
<div class="col-xs-2 pl0">
<div id="selectData" class="tags-input"></div>
</div>
</div>
</div>
</div>
</div>
<% } %>
<script>
var selectData = ${isNotBlank(selectData!) ? selectData! : "{\}"},
selectNum = 0, dataGrid = $('#dataGrid').dataGrid({
searchForm: $("#searchForm"),
columnModel: [
{header:'${text('登录账号')}', name:'loginCode', index:'a.login_code', width:200, align:"center"},
{header:'${text('用户昵称')}', name:'userName', index:'a.user_name', width:200, align:"center"},
{header:'${text('员工姓名')}', name:'refName', index:'a.ref_name', width:200, align:"center"},
{header:'${text('归属机构')}', name:'employee.office.officeName', index:'o.office_name', width:200, align:"center"},
{header:'${text('归属公司')}', name:'employee.company.companyName', index:'c.company_name', width:200, align:"center"},
{header:'${text('电子邮箱')}', name:'email', index:'a.email', width:200, align:"center"},
{header:'${text('手机号码')}', name:'mobile', index:'a.mobile', width:200, align:"center"},
{header:'${text('办公电话')}', name:'phone', index:'a.phone', width:200, align:"center"},
{header:'${text('更新时间')}', name:'updateDate', index:'a.update_date', width:200, align:"center"},
{header:'${text('状态')}', name:'status', index:'a.status', width:140, align:"center", formatter: function(val, obj, row, act){
return js.getDictLabel(${@DictUtils.getDictListJson('sys_status')}, val, '未知', true);
}},
{header:'行数据', name:'rowData', hidden:true, formatter: function(val, obj, row, act){
return JSON.stringify(row);
}}
],
autoGridHeight: function(){
var height = $(window).height() - $('#searchForm').height() - $('#dataGridPage').height() - 73;
$('.tags-input').height($('.ui-jqgrid').height() - 10);
return height;
},
showCheckbox: ${checkbox! == 'true'},
multiboxonly: false, // 单击复选框时再多选
ajaxSuccess: function(data){
$.each(selectData, function(key, value){
dataGrid.dataGrid('setSelectRow', key);
});
initSelectTag();
},
onSelectRow: function(id, isSelect, event){
if (${checkbox! == 'true'}){
if(isSelect){
selectData[id] = JSON.parse(dataGrid.dataGrid('getRowData', id).rowData);
}else{
delete selectData[id];
}
}else{
selectData = {};
selectData[id] = JSON.parse(dataGrid.dataGrid('getRowData', id).rowData);
}
initSelectTag();
},
onSelectAll: function(ids, isSelect){
if (${checkbox! == 'true'}){
for (var i=0; i<ids.length; i++){
if(isSelect){
selectData[ids[i]] = JSON.parse(dataGrid.dataGrid('getRowData', ids[i]).rowData);
}else{
delete selectData[ids[i]];
}
}
}
initSelectTag();
},
ondblClickRow: function(id, rownum, colnum, event){
if (${checkbox! != 'true'}){
js.layer.$('#' + window.name).closest('.layui-layer')
.find(".layui-layer-btn0").trigger("click");
}
initSelectTag();
}
});
function initSelectTag(){
selectNum = 0;
var html = [];
$.each(selectData, function(key, value){
selectNum ++;
html.push('<span class="tag" id="'+key+'_tags-input"><span>'+value.userName+' </span>'
+ '<a href="#" onclick="removeSelectTag(\''+key+'\');" title="取消选择">x</a></span>');
});
html.unshift('<div class="title">当前已选择<span id="selectNum">'+selectNum+'</span>项:</div>');
$('#selectData').empty().append(html.join(''));
}
function removeSelectTag(key){
delete selectData[key];
dataGrid.dataGrid('resetSelection', key);
$('#selectNum').html(--selectNum);
$('#'+key+'_tags-input').remove();
}
function getSelectData(){
return selectData;
}
</script>

View File

@@ -24,7 +24,7 @@ var bodyClass = 'fixed noscroll2 sidebar-mini ' + sidebarCollapse;
</div>
<% } %>
<div class="hide" id="desktopTabPage" data-title="${text('仪表盘')}"
data-url="${ctx}${@Global.getConfig('sys.index.desktopUrl', '/desktop')}"></div>
data-url="${ctx}${@Global.getConfig('sys.index.desktopUrl')}"></div>
<div class="hide" id="modifyPasswordTip" data-message="${modifyPasswordTip!}"></div>
<script src="${ctxStatic}/jquery-toastr/2.0/toastr.min.js?${_version}"></script>
<script src="${ctxStatic}/jquery-plugins/jquery.slimscroll.js"></script>

View File

@@ -1,7 +1,6 @@
<% var productName = @Global.getConfig('productName'); %>
<nav class="navbar navbar-static-top">
<div class="logo" data-toggle="push-menu" title="${productName}">
<b>${productName}</b>
<div class="logo" data-toggle="push-menu">
<b>${@Global.getConfig('productName')}</b>
<small>&nbsp; &nbsp;<i class="fa fa-bars"></i></small>
</div>
<%/*<!--%><a href="javascript:" class="sidebar-toggle" data-toggle="push-menu" role="button">

View File

@@ -5,7 +5,8 @@
</a>
<script>
function refreshOnlineCount(){
$.get('${ctx}/sys/online/count?__notUpdateSession=true&__t='+new Date().getTime(), function(data){
$.get('${ctx}/sys/online/count?__notUpdateSession=true&__t='
+ new Date().getTime(), function(data){
try{$('#onlineCount').html(Number(data))}catch(e){}
})
}

View File

@@ -3,10 +3,10 @@
<link rel="stylesheet" href="${ctxStatic}/icheck/1.0/square/blue.css?${_version}">
<link rel="stylesheet" href="${ctxStatic}/jquery-toastr/2.0/toastr.min.css?${_version}">
<link rel="stylesheet" href="${ctxStatic}/modules/sys/sysLogin.css?${_version}">
<% var productName = @Global.getConfig('productName'), productVersion = @Global.getConfig('productVersion'); %>
<div class="login-box">
<div class="login-logo" title="${productName}">
<a href="${ctx}/login"><b>${productName}</b> <small>${productVersion}</small></a>
<div class="login-logo">
<a href="${ctx}/login"><b>${@Global.getConfig('productName')}</b>
<small>${@Global.getConfig('productVersion')}</small></a>
</div>
<div class="login-box-body">
<#form:form id="loginForm" model="${user!}" action="${ctx}/login" method="post">
@@ -24,7 +24,7 @@
<#form:input type="password" name="password" class="form-control required"
data-msg-required="${text('请填写登录密码.')}" placeholder="${text('登录密码')}" autocomplete="off"/>
</div>
<%/*<!-- if(@ObjectUtils.toBoolean(@Global.getConfig('user.useCorpModel'))){ %>
<%/*<!-- if(@Global.getConfigToBoolean('user.useCorpModel', 'false')){ %>
<div class="form-group has-feedback">
<select name="param_corpCode" class="form-control">
<% for(var user in @UserUtils.findCorpList()){ %>
@@ -54,9 +54,10 @@
</#form:form>
<div class="row">
<div class="col-xs-12">
<% if(@ObjectUtils.toBoolean(@Global.getConfig('user.registerUser'))){ %>
<a href="${ctxPath}/account/registerUser" class="pull-right">${text('注册账号')}</a><% } %>
<a href="${ctxPath}/account/forgetPwd" class="pull-left">${text('忘记密码')}</a>
<a href="${ctxPath}/account/forgetPwd" class="pull-left">[ ${text('忘记密码')} ]</a>
<% if(@Global.getConfigToBoolean('user.registerUser', 'false')){ %>
<a href="${ctxPath}/account/registerUser" class="pull-left ml10">[ ${text('注册账号')} ]</a>
<% } %>
<%
var langTypeList = @DictUtils.getDictList('sys_lang_type');
if (langTypeList.~size > 1){
@@ -78,7 +79,8 @@
</div>
</div>
<div class="login-copyright">
&copy; ${@DateUtils.getYear()} ${productName} - Powered By <a href="http://jeesite.com">JeeSite</a>.
&copy; ${@DateUtils.getYear()} ${@Global.getConfig('productName')} - Powered By <a
href="http://jeesite.com">JeeSite ${@Global.getProperty('jeesiteVersion')}</a>
</div>
</div>
<% } %>

View File

@@ -29,7 +29,7 @@ import com.jeesite.modules.sys.entity.User;
/**
* Mapper测试
* @author ThinkGem
* @version 2017年2月25日
* @version 2018-08-11
*/
@ActiveProfiles("test")
@SpringBootTest(classes=ApplicationTest.class)
@@ -50,7 +50,7 @@ public class DaoMapperTest extends BaseSpringContextTests {
public void testTableAnnotation() throws Exception{
try{
System.out.println("============ 插入批量插入测试 ============");
System.out.println("============ 插入批量插入测试 ============");
Config config = new Config();
config.setId("1");
config.setConfigKey("test");
@@ -64,13 +64,13 @@ public class DaoMapperTest extends BaseSpringContextTests {
System.out.println(configDao.insert(config));
System.out.println(configDao.insertBatch(ListUtils.newArrayList(config2, config3)));
System.out.println("============ 更新、删除测试 ============");
System.out.println("============ 更新测试 ============");
Area area = new Area();
area.setAreaCode("1");
area.setAreaName("你好");
area.setStatus("0");
Area where = new Area();
where.setAreaCode("2");
where.setId("2");
where.setId_in(new String[]{"1","2"});
where.setAreaName("你好2");
where.setStatus("0");
@@ -78,12 +78,18 @@ public class DaoMapperTest extends BaseSpringContextTests {
System.out.println(areaDao.updateByEntity(area, where));
System.out.println(areaDao.updateStatus(area));
System.out.println(areaDao.updateStatusByEntity(area, where));
System.out.println("============ 逻辑删除测试 ============");
System.out.println(areaDao.delete(area));
System.out.println(areaDao.delete(where));
System.out.println(areaDao.deleteByEntity(where));
System.out.println(areaDao.findList(area));
System.out.println(areaDao.delete((Area)where.clone()));
System.out.println(areaDao.deleteByEntity((Area)where.clone()));
System.out.println("============ 物理删除测试 ============");
System.out.println(areaDao.phyDelete((Area)where.clone()));
System.out.println(areaDao.phyDeleteByEntity((Area)where.clone()));
System.out.println("============ 基本信息查询测试 ============");
System.out.println(areaDao.findList(area));
User user = new User();
user.setUserType(User.USER_TYPE_NONE);
System.out.println(userDao.findList(user));

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>4.0.4-SNAPSHOT</version>
<version>4.0.6-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
<!-- ====== 这是一个新增模块示例项目,你可以拷贝此项目,修改 artifactId 为您的模块即可 ====== -->

View File

@@ -11,7 +11,7 @@
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>4.0.4-SNAPSHOT</version>
<version>4.0.6-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JeeSite Parent</name>
@@ -94,7 +94,7 @@
</goals> -->
<configuration>
<!-- <classesDirectory>${project.outputDirectory}</classesDirectory>
<finalName>jeesite</finalName>
<finalName>${finalName}</finalName>
<outputDirectory>${project.build.directory}/${project.artifactId}/WEB-INF/lib</outputDirectory>
<includes>
<include>com/thinkgem/jeesite/**</include>

View File

@@ -1,2 +0,0 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -3,14 +3,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>4.0.4-SNAPSHOT</version>
<relativePath>parent/pom.xml</relativePath>
</parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-root</artifactId>
<version>4.0.6-SNAPSHOT</version>
<packaging>pom</packaging>
<name>JeeSite</name>

View File

@@ -3,15 +3,13 @@
1. 不得应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
2. 付费之前请确认已通过社区版了解和试用“产品”,并确认产品功能符合您的需求;付费之后源代码与社区版本相同,不另外提供源代码,您只需上传许可文件即可;许可文件一经发出概不提供退货服务。
3. 您必须了解使用本软件的风险本软件无法保证100%没有漏洞,所以由软件漏洞造成的损失不予赔偿,也不承担任何因使用本软件而产生相关法律责任。请软件上线使用前进行足够的安全测试,以避免此问题发生。
4. 若您已经购买本产品许可文件或以其它方式获得的许可文件,将被视为您对本服务条款全部的完全接受,如果您未能遵守本服务条款,您的许可授权将被终止,许可的权利将被收回,同时您应承担相应法律责任
5. JeeSite官方对以上条款有最终的解释权
4. 基于JeeSite开发的项目或产品名称以及公司名称同意支持和参与JeeSite推广的案例公布
5. 若您已经购买本产品许可文件或以其它方式获得的许可文件,将被视为您对本服务条款全部的完全接受,如果您未能遵守本服务条款,您的许可授权将被终止,许可的权利将被收回,同时您应承担相应法律责任
# 联系方式
QQ78112665
QQ1766571055
**请认准唯一捐赠收款账号**ThinkGem*震)
**请认准唯一收款账号(其它付款均不作为有效凭证)**
![ThinkGem的支付宝收款二维码](https://static.oschina.net/uploads/img/201803/16112020_sFWX.jpg "ThinkGem的支付宝收款二维码")
![ThinkGem的支付宝收款二维码](https://static.oschina.net/uploads/img/201803/12160758_glou.jpg "ThinkGem的支付宝收款二维码")

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="jeesite-web">
<dependent-module archiveName="jeesite-module-core-4.0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/jeesite-module-core/jeesite-module-core">
<wb-module deploy-name="web">
<dependent-module archiveName="jeesite-module-core-4.0.6-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/jeesite-module-core/jeesite-module-core">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module archiveName="jeesite-common-4.0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/jeesite-common/jeesite-common">
<dependent-module archiveName="jeesite-common-4.0.6-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/jeesite-common/jeesite-common">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module archiveName="jeesite-framework-4.0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/jeesite-framework/jeesite-framework">
<dependent-module archiveName="jeesite-framework-4.0.6-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/jeesite-framework/jeesite-framework">
<dependency-type>uses</dependency-type>
</dependent-module>
<property name="component.exclusion.patterns"/>
<property name="java-output-path" value="/src/main/webapp/WEB-INF/classes"/>
<property name="context-root" value="jeesite-web"/>
<property name="context-root" value="web"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="src/main/resources"/>
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>

View File

@@ -36,6 +36,6 @@ echo.
cd %~dp0
cd ..
call mvn test -Dtest=com.jeesite.test.InitCoreData -U
call mvn test -Dmaven.test.skip=false -Dtest=com.jeesite.test.InitCoreData -Djeesite.initdata=true -U
pause

View File

@@ -29,4 +29,4 @@ read -s -n1 -p "请按任意键继续 ... "
echo ""
cd ..
mvn test -Dtest=com.jeesite.test.InitCoreData -U
mvn test -Dmaven.test.skip=false -Dtest=com.jeesite.test.InitCoreData -Djeesite.initdata=true -U

View File

@@ -11,13 +11,39 @@ echo.
%~d0
cd %~dp0
rem <20><><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̣<EFBFBD><CCA3><EFBFBD>ʼ<EFBFBD><CABC>
cd ..
call mvn clean package spring-boot:repackage -Dmaven.test.skip=true -U
cd target
call unzip -n *.war -d web
rem <20><><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
cd web/WEB-INF
call startup.bat
title %cd%
rem <20>Ż<EFBFBD>JVM<56><4D><EFBFBD><EFBFBD>
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
rem <20><>ʽһ<CABD><D2BB><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
rem set JAVA_OPTS=%JAVA_OPTS% -Dspring.profiles.active=prod
rem <20><>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ⲿ<EFBFBD><E2B2BF><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
rem set JAVA_OPTS=%JAVA_OPTS% -Dspring.config.location=%cd%\app.yml
if "%JAVA_HOME%" == "" goto noJavaHome
if not "%JAVA_HOME%" == "" goto gotJavaHome
goto end
:noJavaHome
set RUN_JAVA=java
goto runJava
:gotJavaHome
set RUN_JAVA="%JAVA_HOME%\bin\java"
goto runJava
:runJava
rem <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>޸<EFBFBD> web.war Ϊ<><CEAA><EFBFBD><EFBFBD> war <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
call "%RUN_JAVA%" %JAVA_OPTS% -jar web.war
goto end
:end
pause

View File

@@ -6,15 +6,29 @@
# *
# */
echo ""
echo "[信息] 使用 Spring Boot Tomcat 运行 Web 工程。"
echo "[信息] 打包Web工程运行Web工程。"
echo ""
# 打包Web工程开始
cd ..
mvn clean package spring-boot:repackage -Dmaven.test.skip=true -U
cd target
unzip -n *.war -d web
# 打包Web工程结束
cd web/WEB-INF
chmod +x *.sh
./startup.sh
# 优化JVM参数
JAVA_OPTS="$MAVEN_OPTS -Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m"
# 方式一、配置环境名称
# JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod"
# 方式二、配置外部属性文件(建议)
# JAVA_OPTS="$JAVA_OPTS -Dspring.config.location=$PWD\app.yml"
if [ -z "$JAVA_HOME" ]; then
RUN_JAVA=java
else
RUN_JAVA="$JAVA_HOME"/bin/java
fi
# 根据情况修改 web.war 为您的 war 包名称
exec $RUN_JAVA $JAVA_OPTS -jar web.war

View File

@@ -175,7 +175,7 @@ CREATE TABLE [js_sys_config]
[id] varchar(64) NOT NULL,
[config_name] nvarchar(100) NOT NULL,
[config_key] varchar(100) NOT NULL,
[config_value] nvarchar(1000) NOT NULL,
[config_value] nvarchar(1000),
[is_sys] char(1) NOT NULL,
[create_by] varchar(64) NOT NULL,
[create_date] datetime NOT NULL,
@@ -788,8 +788,8 @@ CREATE TABLE [js_sys_user_role]
/* Create Indexes */
CREATE INDEX [idx_gen_table_ptn] ON [js_gen_table] ();
CREATE INDEX [idx_gen_table_column_tn] ON [js_gen_table_column] ();
CREATE INDEX [idx_gen_table_ptn] ON [js_gen_table] ([parent_table_name]);
CREATE INDEX [idx_gen_table_column_tn] ON [js_gen_table_column] ([table_name]);
CREATE INDEX [idx_sys_area_pc] ON [js_sys_area] ([parent_code]);
CREATE INDEX [idx_sys_area_ts] ON [js_sys_area] ([tree_sort]);
CREATE INDEX [idx_sys_area_status] ON [js_sys_area] ([status]);

View File

@@ -176,7 +176,7 @@ CREATE TABLE js_sys_config
id varchar(64) NOT NULL COMMENT '编号',
config_name varchar(100) NOT NULL COMMENT '名称',
config_key varchar(100) NOT NULL COMMENT '参数键',
config_value varchar(1000) NOT NULL COMMENT '参数值',
config_value varchar(1000) COMMENT '参数值',
is_sys char(1) NOT NULL COMMENT '系统内置1是 0否',
create_by varchar(64) NOT NULL COMMENT '创建者',
create_date datetime NOT NULL COMMENT '创建时间',
@@ -790,8 +790,8 @@ CREATE TABLE js_sys_user_role
/* Create Indexes */
CREATE INDEX idx_gen_table_ptn ON js_gen_table ();
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column ();
CREATE INDEX idx_gen_table_ptn ON js_gen_table (parent_table_name ASC);
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column (table_name ASC);
CREATE INDEX idx_sys_area_pc ON js_sys_area (parent_code ASC);
CREATE INDEX idx_sys_area_ts ON js_sys_area (tree_sort ASC);
CREATE INDEX idx_sys_area_status ON js_sys_area (status ASC);

View File

@@ -175,7 +175,7 @@ CREATE TABLE js_sys_config
id varchar2(64) NOT NULL,
config_name nvarchar2(100) NOT NULL,
config_key varchar2(100) NOT NULL,
config_value nvarchar2(1000) NOT NULL,
config_value nvarchar2(1000),
is_sys char(1) NOT NULL,
create_by varchar2(64) NOT NULL,
create_date timestamp NOT NULL,
@@ -788,8 +788,8 @@ CREATE TABLE js_sys_user_role
/* Create Indexes */
CREATE INDEX idx_gen_table_ptn ON js_gen_table ();
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column ();
CREATE INDEX idx_gen_table_ptn ON js_gen_table (parent_table_name);
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column (table_name);
CREATE INDEX idx_sys_area_pc ON js_sys_area (parent_code);
CREATE INDEX idx_sys_area_ts ON js_sys_area (tree_sort);
CREATE INDEX idx_sys_area_status ON js_sys_area (status);

View File

@@ -175,7 +175,7 @@ CREATE TABLE js_sys_config
id varchar(64) NOT NULL,
config_name varchar(100) NOT NULL,
config_key varchar(100) NOT NULL,
config_value varchar(1000) NOT NULL,
config_value varchar(1000),
is_sys char(1) NOT NULL,
create_by varchar(64) NOT NULL,
create_date timestamp NOT NULL,
@@ -788,8 +788,8 @@ CREATE TABLE js_sys_user_role
/* Create Indexes */
CREATE INDEX idx_gen_table_ptn ON js_gen_table ();
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column ();
CREATE INDEX idx_gen_table_ptn ON js_gen_table (parent_table_name);
CREATE INDEX idx_gen_table_column_tn ON js_gen_table_column (table_name);
CREATE INDEX idx_sys_area_pc ON js_sys_area (parent_code);
CREATE INDEX idx_sys_area_ts ON js_sys_area (tree_sort);
CREATE INDEX idx_sys_area_status ON js_sys_area (status);

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>4.0.4-SNAPSHOT</version>
<version>4.0.6-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@@ -19,6 +19,7 @@
<properties>
<finalName>web</finalName><!-- war包的名称 ${project.artifactId} -->
<start-class>com.jeesite.modules.config.Application</start-class>
<!-- environment setting -->
@@ -44,20 +45,6 @@
<version>${project.parent.version}</version>
</dependency>
<!-- 内容管理模块
<dependency>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-module-cms</artifactId>
<version>${project.parent.version}</version>
</dependency>-->
<!-- 微信模块
<dependency>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-module-weixin</artifactId>
<version>${project.parent.version}</version>
</dependency>-->
<!-- 自定义jar依赖包演示
<dependency>
<groupId>com.jeesite</groupId>
@@ -70,7 +57,7 @@
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<finalName>${finalName}</finalName>
<outputDirectory>${project.basedir}/src/main/webapp/WEB-INF/classes/</outputDirectory>
<plugins>
@@ -84,6 +71,21 @@
<artifactId>maven-shade-plugin</artifactId>
</plugin> -->
<!-- 打包插件, war包名称不带版本号 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes></packagingExcludes>
<warSourceExcludes></warSourceExcludes>
<webappDirectory>${project.build.directory}/${project.artifactId}</webappDirectory>
<warName>${finalName}</warName>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<!-- Eclipse插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -91,7 +93,7 @@
<configuration>
<downloadSources>${eclipse-plugin-download-sources}</downloadSources>
<downloadJavadocs>${eclipse-plugin-download-javadocs}</downloadJavadocs>
<wtpContextName>${project.artifactId}</wtpContextName>
<wtpContextName>${finalName}</wtpContextName>
<wtpversion>2.0</wtpversion>
<jeeversion>6.0</jeeversion>
</configuration>

View File

@@ -1,31 +0,0 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
*/
package com.jeesite.modules.config.web;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextListener;
/**
* Listener 配置
* @author ThinkGem
* @version 2017年11月29日
*/
@Configuration
public class ListenerConfig {
/**
* Request Context Listener
*/
@Bean
@Order(1000)
public ServletListenerRegistrationBean<RequestContextListener> requestContextListener() {
ServletListenerRegistrationBean<RequestContextListener> bean = new ServletListenerRegistrationBean<>();
bean.setListener(new RequestContextListener());
return bean;
}
}

View File

@@ -1,16 +0,0 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
*/
package com.jeesite.modules.config.web;
import org.springframework.context.annotation.Configuration;
/**
* Servlet 配置
* @author ThinkGem
* @version 2017年11月30日
*/
@Configuration
public class ServletConfig {
}

View File

@@ -3,6 +3,9 @@
*/
package com.jeesite.modules.test.dao;
import java.util.List;
import java.util.Map;
import com.jeesite.common.dao.CrudDao;
import com.jeesite.common.mybatis.annotation.MyBatisDao;
import com.jeesite.modules.test.entity.TestData;
@@ -15,4 +18,9 @@ import com.jeesite.modules.test.entity.TestData;
@MyBatisDao
public interface TestDataDao extends CrudDao<TestData> {
/**
* 演示Map参数和返回值支持分页
*/
public List<Map<String, Object>> findListForMap(Map<String, Object> params);
}

View File

@@ -31,7 +31,7 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
@Column(name="test_select_multiple", attrName="testSelectMultiple", label="下拉多选"),
@Column(name="test_radio", attrName="testRadio", label="单选框"),
@Column(name="test_checkbox", attrName="testCheckbox", label="复选框"),
@Column(name="test_date", attrName="testDate", label="日期选择"),
@Column(name="test_date", attrName="testDate", label="日期选择", isUpdateForce=true),
@Column(name="test_datetime", attrName="testDatetime", label="日期时间"),
@Column(name="test_user_code", attrName="testUser.userCode", label="用户选择"),
@Column(name="test_office_code", attrName="testOffice.officeCode", label="机构选择"),

View File

@@ -51,6 +51,16 @@ public class TestDataService extends CrudService<TestDataDao, TestData> {
*/
@Override
public Page<TestData> findPage(Page<TestData> page, TestData testData) {
// // 演示Map参数和返回值支持分页
// Page<Map<String, Object>> pageMap = new Page<>();
// Map<String, Object> params = MapUtils.newHashMap();
// params.put("testInput", "123");
// params.put("page", pageMap);
// pageMap.setList(dao.findListForMap(params));
// System.out.println(pageMap.getList());
// System.out.println(pageMap.getCount());
return super.findPage(page, testData);
}

View File

@@ -3,17 +3,23 @@
#===== Project settings =====#
#============================#
# 产品或项目名称、版本、版权年份
# 产品或项目名称、软件开发公司名称
productName: JeeSite Demo
companyName: ThinkGem
# 产品版本、版权年份
productVersion: V4.0
copyrightYear: 2018
# 软件提供商公司或个人名称
companyName: ThinkGem
#是否演示模式
demoMode: false
#是否小程序
miniService: true
#是否小程序
miniConfig: true
#============================#
#===== Database sttings =====#
#============================#
@@ -102,8 +108,9 @@ jdbc:
# stat:
# enabled: true
# Redis 配置
# Redis 配置2.x/3.x
#redis:
# enabled: false
#
# # Redis 连接参数
# host: 127.0.0.1
@@ -129,13 +136,13 @@ jdbc:
#===== System settings ======#
#============================#
##管理基础路径
# 管理基础路径
#adminPath: /a
#
##前端基础路径
# 前端基础路径
#frontPath: /f
#
## 分页配置
# 分页配置
#page:
#
# # 分页默认大小
@@ -197,7 +204,7 @@ jdbc:
# expandLevel: -1,
# remarks: ""
# }]
#
# # 多租户模式SAAS模式专业版
# useCorpModel: false
@@ -218,6 +225,7 @@ jdbc:
# # 任务调度集群设置
# jobStore:
# isClustered: true
# dataSourceName: job
# clusterCheckinInterval: 1000
#
@@ -225,7 +233,7 @@ jdbc:
#==== Framework settings ====#
#============================#
## Shiro 相关配置
# Shiro 相关配置
#shiro:
#
# #索引页路径
@@ -236,14 +244,14 @@ jdbc:
# logoutUrl: ${shiro.loginUrl}
# successUrl: ${adminPath}/index
#
# # CAS 相关配置
## casServerUrl: http://192.168.1.3:8080/cas
## casClientUrl: http://192.168.1.3:8180/jeesite
## # Jasig CAS 相关配置
## casServerUrl: http://127.0.0.1:8981/cas
## casClientUrl: http://127.0.0.1:8980/js
## loginUrl: ${shiro.casServerUrl}?service=${shiro.casClientUrl}${adminPath}/login-cas
## logoutUrl: ${shiro.casServerUrl}/logout?service=${shiro.loginUrl}
## successUrl: ${shiro.casClientUrl}${adminPath}/index
#
# # SSO 登录相关配置
# # 简单 SSO 登录相关配置
# sso:
#
# # 如果启用/sso/{username}/{token}单点登录请修改此安全key并与单点登录系统key一致。
@@ -252,12 +260,15 @@ jdbc:
# # 是否加密单点登录安全Key
# encryptKey: true
#
# # 登录提交信息加密
# # 登录提交信息加密(如果不需要加密,设置为空即可)
# loginSubmit:
#
# # 登录提交信息安全Key加密用户名、密码、验证码后再提交key设置为3个用逗号分隔
# secretKey: thinkgem,jeesite,com
#
# # 指定获取客户端IP的Header名称防止IP伪造。指定为空则使用原生方法获取IP。
# remoteAddrHeaderName: X-Forwarded-For
#
# # 允许的请求方法设定,解决安全审计问题
# allowRequestMethods: GET,POST
#
@@ -273,13 +284,16 @@ jdbc:
# # 是否允许跨域访问,如果允许,设置允许的域名,全部域名设置*号,如果不允许,此设置应该为空
## accessControlAllowOrigin: http://demo.jeesite.com
## accessControlAllowOrigin: '*'
#
# # 是否在登录后生成新的Session默认false
# isGenerateNewSessionAfterLogin: false
#
# # URI 权限过滤器定义
# filterChainDefinitions: |
# /ReportServer/** = user
# ${adminPath}/** = user
#
## Session 相关
# Session 相关
#session:
#
# #全局会话超时,单位:毫秒, 20m=1200000ms, 30m=1800000ms, 60m=3600000ms, 12h=43200000ms, 1day=86400000ms
@@ -297,7 +311,7 @@ jdbc:
# #共享的SessionId的Cookie名称保存到跟路径下第三方应用获取。同一域名下多个项目时需设置共享Cookie的名称。
# #shareSessionIdCookieName: ${session.sessionIdCookieName}
#
## MyBatis 相关
# MyBatis 相关
#mybatis:
#
# # 扫描基础包设置Aliases、@MyBatisDao如果多个用“,”分隔
@@ -311,7 +325,7 @@ jdbc:
# sleepSeconds: 3
# mappingPath: mappings
#
## 缓存设置
# 缓存设置
#ehcache:
#
# # 缓存配置文件路径
@@ -326,7 +340,7 @@ jdbc:
# enabled: false
# urlPatterns: "*.html"
#
## Web 相关
# Web 相关
#web:
#
# # MVC 视图相关
@@ -350,6 +364,7 @@ jdbc:
# ${adminPath}/login,
# ${adminPath}/desktop,
# ${adminPath}/sys/online/count,
# ${adminPath}/state/server/rtInfo,
# ${adminPath}/**/treeData,
# ${adminPath}/file/**,
# ${adminPath}/tags/*,
@@ -368,7 +383,7 @@ jdbc:
# # 静态文件后缀排除的url路径指定哪些uri路径不进行静态文件过滤。
# staticFileExcludeUri: /druid/
#
## 错误页面500.html是否输出错误信息正式环境为提供安全性可设置为false
# 错误页面500.html是否输出错误信息正式环境为提供安全性可设置为false
#error:
# page:
# printErrorInfo: true

View File

@@ -12,4 +12,17 @@
ORDER BY ${sqlMap.order.toSql()}
</select> -->
<!-- 演示Map参数和返回值支持分页 -->
<select id="findListForMap" resultType="map">
SELECT * FROM test_data a
<where>
<if test="testInput != null and testInput != ''">
AND a.test_input = #{testInput}
</if>
</where>
<if test="page != null and page.orderBy != null and page.orderBy != ''">
ORDER BY ${page.orderBy}
</if>
</select>
</mapper>

View File

@@ -82,7 +82,7 @@
<span class="required hide">*</span> ${text('日期选择')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:input path="testDate" readonly="true" maxlength="20" class="form-control Wdate"
dataFormat="date" onclick="WdatePicker({dateFmt:'yyyy-MM-dd',isShowClear:false});"/>
dataFormat="date" onclick="WdatePicker({dateFmt:'yyyy-MM-dd',isShowClear:true});"/>
</div>
</div>
</div>

View File

@@ -150,7 +150,7 @@ $('#dataGrid').dataGrid({
}},
{header:'${text('创建时间')}', name:'createDate', index:'a.create_date', width:150, align:"center"},
{header:'${text('备注信息')}', name:'remarks', index:'a.remarks', width:150, align:"left"},
{header:'${text('操作')}', name:'actions', width:120, sortable:false, title:false, formatter: function(val, obj, row, act){
{header:'${text('操作')}', name:'actions', width:200, sortable:false, title:false, formatter: function(val, obj, row, act){
var actions = [];
<% if(hasPermi('test:testData:edit')){ %>
actions.push('<a href="${ctx}/test/testData/form?id='+row.id+'" class="btnList" title="${text('编辑数据')}"><i class="fa fa-pencil"></i></a>&nbsp;');

View File

@@ -16,8 +16,15 @@ cd %~dp0
title %cd%
rem <20>Ż<EFBFBD>JVM<56><4D><EFBFBD><EFBFBD>
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
rem <20><>ʽһ<CABD><D2BB><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
rem set JAVA_OPTS=%JAVA_OPTS% -Dspring.profiles.active=prod
rem <20><>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ⲿ<EFBFBD><E2B2BF><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
rem set JAVA_OPTS=%JAVA_OPTS% -Dspring.config.location=%cd%\app.yml
if "%JAVA_HOME%" == "" goto noJavaHome
if not "%JAVA_HOME%" == "" goto gotJavaHome
goto end

View File

@@ -9,12 +9,19 @@ echo ""
echo "[信息] 运行Web工程。"
echo ""
# 优化JVM参数
JAVA_OPTS="$MAVEN_OPTS -Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m"
# 方式一、配置环境名称
# JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod"
# 方式二、配置外部属性文件(建议)
# JAVA_OPTS="$JAVA_OPTS -Dspring.config.location=$PWD\app.yml"
if [ -z "$JAVA_HOME" ]; then
RUN_JAVA=java
else
RUN_JAVA="$JAVA_HOME"/bin/java
fi
exec $RUN_JAVA -cp `dirname $0`/../ $JAVA_OPTS org.springframework.boot.loader.WarLauncher
exec $RUN_JAVA -cp $PWD/../ $JAVA_OPTS org.springframework.boot.loader.WarLauncher