xuhy
2025-01-09 6e2a9c1db7106b0ca2ffa24a6e24440976c75629
初始化
35个文件已添加
1673 ■■■■■ 已修改文件
DispatchOKTravel/.gitattributes 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/.gitignore 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/LICENSE 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/README.md 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/db/guns_rest.sql 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/pom.xml 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/GunsRestApplication.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/GunsRestServletInitializer.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/SimpleObject.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/aop/GlobalExceptionHandler.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/exception/BizExceptionEnum.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/persistence/dao/UserMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/persistence/dao/mapping/UserMapper.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/persistence/model/User.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/MessageConverConfig.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/MybatisPlusConfig.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/WebConfig.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/properties/JwtProperties.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/properties/RestProperties.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/controller/AuthController.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/controller/dto/AuthRequest.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/controller/dto/AuthResponse.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/converter/BaseTransferEntity.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/converter/WithSignMessageConverter.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/filter/AuthFilter.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/security/DataSecurityAction.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/security/impl/Base64SecurityAction.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/util/JwtTokenUtil.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/IReqValidator.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/dto/Credence.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/impl/DbValidator.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/impl/SimpleValidator.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/example/ExampleController.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/guns-rest/src/main/resources/application.yml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/pom.xml 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
DispatchOKTravel/.gitattributes
New file
@@ -0,0 +1,4 @@
*.js linguist-language=java
*.css linguist-language=java
*.html linguist-language=java
*.btl linguist-language=java
DispatchOKTravel/.gitignore
New file
@@ -0,0 +1,33 @@
# Compiled class file
*.class
*.iml
*.idea
target/
logs/
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*velocity.log*
# Eclipse #
.classpath
.project
.settings/
DispatchOKTravel/LICENSE
New file
@@ -0,0 +1,13 @@
Copyright 2018 StyleFeng
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
DispatchOKTravel/README.md
New file
@@ -0,0 +1,186 @@
# Guns V4.1
## 介绍
Guns基于SpringBoot,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + mybatis-plus + beetl!Guns项目代码简洁,注释丰富,上手容易,同时Guns包含许多基础模块(用户管理,角色管理,部门管理,字典管理等10个模块),可以直接作为一个后台管理系统的脚手架! 2018目标 `更简洁`,`更规范`!
Guns v3.0新增rest api服务,提供对接服务端接口的支持,并利用jwt token鉴权机制给予客户端的访问权限,传输数据进行md5签名保证传输过程数据的安全性!
### Guns v4.1更新内容
> * guns-admin提供rest api服务,并以jwt方式鉴权,所有以`/gunsApi`开头的接口走这种鉴权方式,其他接口仍为shiro鉴权
### Guns v4.0更新内容
> * spring boot升级到2.0版本!
> * 简化前端所有的html,js,css等资源文件,由11.8MB缩减为4.8MB!
> * 新增手动增加标签页的方法,Feng.newCrontab(href,menuName);
> * laydate时间插件和layer弹出层插件更新为官网最新,可以看着layui官网的文档直接开发啦!
> * 去掉animate动画效果,加快页面响应速度!
> * 修复登陆后重启应用造成404的bug!
> * 增强core模块的功能,拿来即用开发新的模块的后端管理系统!
> * 升级各个依赖包的版本!
> * 简化applicatioin.yml的配置,一些固定不变的配置写在core模块的default-config.properties!
> * 简化多数据源配置,简化mybatis-plus的配置!
> * 优化日志记录格式和策略,详情见logback-spring.xml!
此外,**更新Guns文档到《Guns技术文档 v2.0》**
## 最新Guns技术文档
Guns框架有作者在业余时间整理的技术文档,详情点击[查看详情](https://gitee.com/naan1993/guns/wikis/pages?title=Guns%E6%8A%80%E6%9C%AF%E6%96%87%E6%A1%A3&parent=)
## 最新Guns视频教程
Guns框架有作者在业余时间录制的视频教程,详情点击[教程介绍](https://gitee.com/naan1993/guns/wikis/pages?title=Guns%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B&parent=)
## 技术讨论 & [wiki地址](https://gitee.com/naan1993/guns/wikis/Home)
如果对项目有任何疑问或者建议,欢迎加入Guns技术交流1群:254550081,Guns技术交流2群:684163663,Guns技术交流3群:207434260(加之前先看下文档guns-admin/doc/Guns技术文档 v1.0.pdf)
## 管理系统功能
1.用户管理 2.角色管理 3.部门管理 4.菜单管理 5.字典管理 6.业务日志 7.登录日志 8.监控管理 9.通知管理 10.代码生成
## 项目特点
1. 基于SpringBoot,简化了大量项目配置和maven依赖,让您更专注于业务开发,独特的分包方式,代码多而不乱。
2. 完善的日志记录体系,可记录登录日志,业务操作日志(可记录操作前和操作后的数据),异常日志到数据库,通过@BussinessLog注解和LogObjectHolder.me().set()方法,业务操作日志可具体记录哪个用户,执行了哪些业务,修改了哪些数据,并且日志记录为异步执行,详情请见@BussinessLog注解和LogObjectHolder,LogManager,LogAop类。
3. 利用beetl模板引擎对前台页面进行封装和拆分,使臃肿的html代码变得简洁,更加易维护。
4. 对常用js插件进行二次封装,使js代码变得简洁,更加易维护,具体请见webapp/static/js/common文件夹内js代码。
5. 利用ehcache框架对经常调用的查询进行缓存,提升运行速度,具体请见ConstantFactory类中@Cacheable标记的方法。
6. controller层采用map + warpper方式的返回结果,返回给前端更为灵活的数据,具体参见com.stylefeng.guns.modular.system.warpper包中具体类。
7. 防止XSS攻击,通过XssFilter类对所有的输入的非法字符串进行过滤以及替换。
8. 简单可用的代码生成体系,通过SimpleTemplateEngine可生成带有主页跳转和增删改查的通用控制器、html页面以及相关的js,还可以生成Service和Dao,并且这些生成项都为可选的,通过ContextConfig下的一些列xxxSwitch开关,可灵活控制生成模板代码,让您把时间放在真正的业务上。
9. 控制器层统一的异常拦截机制,利用@ControllerAdvice统一对异常拦截,具体见com.stylefeng.guns.core.aop.GlobalExceptionHandler类。
10. 页面统一的js key-value单例模式写法,每个页面生成一个唯一的全局变量,提高js的利用效率,并且有效防止多个人员开发引起的函数名/类名冲突,并且可以更好地去维护代码。
## 基于javabean方式的spring配置
Guns以简洁为核心,抛弃了传统的易错,臃肿xml配置,采用javabean的方式配置spring,简化了项目的配置,如下示例为配置mybatis-plus和数据源:
```
@Configuration
@MapperScan(basePackages = {"com.stylefeng.guns.modular.*.dao", "com.stylefeng.guns.common.persistence.dao"})
public class MybatisPlusConfig {
    @Autowired
    DruidProperties druidProperties;
    /**
     * mybatis-plus分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setDialectType(DBType.MYSQL.getDb());
        return paginationInterceptor;
    }
}
```
## 业务日志记录
日志记录采用aop(LogAop类)方式对所有包含@BussinessLog注解的方法进行aop切入,会记录下当前用户执行了哪些操作(即@BussinessLog value属性的内容),如果涉及到数据修改,会取当前http请求的所有requestParameters与LogObjectHolder类中缓存的Object对象的所有字段作比较(所以在编辑之前的获取详情接口中需要缓存被修改对象之前的字段信息),日志内容会异步存入数据库中(通过ScheduledThreadPoolExecutor类)。
## beetl对前台页面的拆分与包装
例如,把主页拆分成三部分,每个部分单独一个页面,更加便于维护
```
<!--左侧导航开始-->
    @include("/common/_tab.html"){}
<!--左侧导航结束-->
<!--右侧部分开始-->
    @include("/common/_right.html"){}
<!--右侧部分结束-->
<!--右侧边栏开始-->
    @include("/common/_theme.html"){}
<!--右侧边栏结束-->
```
以及对重复的html进行包装,使前端页面更加专注于业务实现,例如,把所有页面引用包进行提取
```
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit" /><!-- 让360浏览器默认选择webkit内核 -->
<!-- 全局css -->
<link rel="shortcut icon" href="${ctxPath}/static/favicon.ico">
<!-- 全局js -->
<script src="${ctxPath}/static/js/jquery.min.js?v=2.1.4"></script>
<body class="gray-bg">
    <div class="wrapper wrapper-content">
        ${layoutContent}
    </div>
    <script src="${ctxPath}/static/js/content.js?v=1.0.0"></script>
</body>
</html>
```
开发页面时,只需编写如下代码即可
```
@layout("/common/_container.html"){
<div class="row">
    <div class="col-sm-12">
        <div class="ibox float-e-margins">
            <div class="ibox-title">
                <h5>部门管理</h5>
            </div>
            <div class="ibox-content">
               //自定义内容
            </div>
        </div>
    </div>
</div>
<script src="${ctxPath}/static/modular/system/dept/dept.js"></script>
@}
```
以上beetl的用法请参考beetl说明文档。
## 对js常用代码的封装
在webapp/static/js/common目录中,有对常用js代码的封装,例如Feng.js,其中Feng.info(),Feng.success(),Feng.error()三个方法,分别封装了普通提示,成功提示,错误提示的代码,简化了layer提示层插件的使用。
## 极简的图片上传方法
guns对web-upload进行二次封装,让图片的上传功能呢只用2行代码即可实现,如下
```
var avatarUp = new $WebUpload("avatar");
avatarUp.init();
```
具体实现请参考static/js/common/web-upload-object.js
## 独创controller层,map+warpper返回方式
map+warpper方式即为把controller层的返回结果使用BeanKit工具类把原有bean转化为Map的的形式(或者原有bean直接是map的形式),再用单独写的一个包装类再包装一次这个map,使里面的参数更加具体,更加有含义,下面举一个例子,例如,在返回给前台一个性别时,数据库查出来1是男2是女,假如直接返回给前台,那么前台显示的时候还需要增加一次判断,并且前后端分离开发时又增加了一次交流和文档的成本,但是采用warpper包装的形式,可以直接把返回结果包装一下,例如动态增加一个字段sexName直接返回给前台性别的中文名称即可。
## 独创mybatis数据范围拦截器,实现对数据权限的过滤
Guns的数据范围控制是指,对拥有相同角色的用户,根据部门的不同进行相应的数据筛选,如果部门不相同,那么有可能展示出的具体数据是不一致的.所以说Guns对数据范围控制是以部门id为单位来标识的,如何增加数据范围拦截呢?只需在相关的mapper接口的参数中增加一个DataScope对象即可,DataScope中有两个字段,scopeName用来标识sql语句中部门id的字段名称,例如deptiid或者id,另一个字段deptIds就是具体需要过滤的部门id的集合.拦截器原理如下:拦截mapper中包含DataScope对象的方法,获取其原始sql,并做一个包装限制部门id在deptIds范围内的数据进行展示.
## swagger api管理使用说明
swagger会管理所有包含@ApiOperation注解的控制器方法,同时,可利用@ApiImplicitParams注解标记接口中的参数,具体用法请参考CodeController类中的用法。
```
 @ApiOperation("生成代码")
 @ApiImplicitParams({
         @ApiImplicitParam(name = "moduleName", value = "模块名称", required = true, dataType = "String"),
         @ApiImplicitParam(name = "bizChName", value = "业务名称", required = true, dataType = "String"),
         @ApiImplicitParam(name = "bizEnName", value = "业务英文名称", required = true, dataType = "String"),
         @ApiImplicitParam(name = "path", value = "项目生成类路径", required = true, dataType = "String")
 })
 @RequestMapping(value = "/generate", method = RequestMethod.POST)
```
## jwt token鉴权机制
jwt token鉴权机制是指若需要请求服务器接口,必须通过AuthController获取一个请求令牌(jwt token),持有jwt token的用户才可以访问服务器的其他资源,如果没有此令牌,则访问接口会直接忽略,请求获取jwt token时,需要携带credenceName和credenceCode(可以是账号密码,可以是手机号验证码等等),校验credenceName和credenceCode成功后,会颁发给客户端一个jwt token还有一个随机字符串,用于传输过程中对数据进行签名用,签名机制请见下面介绍.基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息.这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利.
## 签名机制
签名机制是指客户端向服务端传输数据中,对传输数据进行md5加密,并且加密过程中利用Auth接口返回的随机字符串进行混淆加密,并把md5值同时附带给服务端,服务端通获取数据之后对数据再进行一次md5加密,若加密结果和客户端传来的数据一致,则认定客户端请求的数据是没有被篡改的,若不一致,则认为被加密的数据是被篡改的
## 效果图
![输入图片说明](https://git.oschina.net/uploads/images/2017/0604/194616_36ed7fd6_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0604/194623_a0761bc3_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0604/194630_640dfd35_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/104015_bdb14c74_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0516/000735_b83c5c46_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103734_bd3e8f6b_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0604/194539_f9bb482a_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103746_6b4129ed_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103755_7729b916_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103801_b8216865_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103807_20bfb868_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103814_67e078bb_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103822_58fd5d91_551203.png "在这里输入图片标题")
![输入图片说明](https://git.oschina.net/uploads/images/2017/0526/103827_d6218c74_551203.png "在这里输入图片标题")
## 曾获荣誉
![输入图片说明](https://gitee.com/uploads/images/2017/1015/151932_f1593f87_551203.jpeg "initpintu_副本_副本.jpg")
DispatchOKTravel/guns-rest/db/guns_rest.sql
New file
@@ -0,0 +1,38 @@
/*
 Navicat MySQL Data Transfer
 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50721
 Source Host           : localhost:3306
 Source Schema         : guns_rest
 Target Server Type    : MySQL
 Target Server Version : 50721
 File Encoding         : 65001
 Date: 26/01/2018 21:16:47
*/
DROP DATABASE IF EXISTS guns_rest;
CREATE DATABASE IF NOT EXISTS guns_rest DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `userName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin');
SET FOREIGN_KEY_CHECKS = 1;
DispatchOKTravel/guns-rest/pom.xml
New file
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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>
    <groupId>com.stylefeng.guns</groupId>
    <artifactId>guns-rest</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
    <name>guns-rest</name>
    <description>guns REST服务器</description>
    <parent>
        <groupId>com.stylefeng</groupId>
        <artifactId>guns-parent</artifactId>
        <version>1.0.0</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>
        <dependency>
            <groupId>com.stylefeng</groupId>
            <artifactId>guns-core</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/GunsRestApplication.java
New file
@@ -0,0 +1,12 @@
package com.stylefeng.guns.rest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GunsRestApplication {
    public static void main(String[] args) {
        SpringApplication.run(GunsRestApplication.class, args);
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/GunsRestServletInitializer.java
New file
@@ -0,0 +1,19 @@
package com.stylefeng.guns.rest;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
 * Guns REST Web程序启动类
 *
 * @author fengshuonan
 * @date 2017年9月29日09:00:42
 */
public class GunsRestServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(GunsRestApplication.class);
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/SimpleObject.java
New file
@@ -0,0 +1,50 @@
package com.stylefeng.guns.rest.common;
/**
 * 测试用的
 *
 * @author fengshuonan
 * @date 2017-08-25 16:47
 */
public class SimpleObject {
    private String user;
    private String name;
    private String tips;
    private Integer age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getTips() {
        return tips;
    }
    public void setTips(String tips) {
        this.tips = tips;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/aop/GlobalExceptionHandler.java
New file
@@ -0,0 +1,35 @@
package com.stylefeng.guns.rest.common.aop;
import com.stylefeng.guns.core.aop.BaseControllerExceptionHandler;
import com.stylefeng.guns.core.base.tips.ErrorTip;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import io.jsonwebtoken.JwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
 * 全局的的异常拦截器(拦截所有的控制器)(带有@RequestMapping注解的方法上都会拦截)
 *
 * @author fengshuonan
 * @date 2016年11月12日 下午3:19:56
 */
@ControllerAdvice
public class GlobalExceptionHandler extends BaseControllerExceptionHandler {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    /**
     * 拦截jwt相关异常
     */
    @ExceptionHandler(JwtException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorTip jwtException(JwtException e) {
        return new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage());
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/exception/BizExceptionEnum.java
New file
@@ -0,0 +1,55 @@
package com.stylefeng.guns.rest.common.exception;
import com.stylefeng.guns.core.exception.ServiceExceptionEnum;
/**
 * 所有业务异常的枚举
 *
 * @author fengshuonan
 * @date 2016年11月12日 下午5:04:51
 */
public enum BizExceptionEnum implements ServiceExceptionEnum {
    /**
     * token异常
     */
    TOKEN_EXPIRED(700, "token过期"),
    TOKEN_ERROR(700, "token验证失败"),
    /**
     * 签名异常
     */
    SIGN_ERROR(700, "签名验证失败"),
    /**
     * 其他
     */
    AUTH_REQUEST_ERROR(400, "账号密码错误");
    BizExceptionEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }
    private Integer code;
    private String message;
    @Override
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    @Override
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/persistence/dao/UserMapper.java
New file
@@ -0,0 +1,16 @@
package com.stylefeng.guns.rest.common.persistence.dao;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.stylefeng.guns.rest.common.persistence.model.User;
/**
 * <p>
  *  Mapper 接口
 * </p>
 *
 * @author stylefeng
 * @since 2017-08-23
 */
public interface UserMapper extends BaseMapper<User> {
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/persistence/dao/mapping/UserMapper.xml
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.stylefeng.guns.rest.common.persistence.dao.UserMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.stylefeng.guns.rest.common.persistence.model.User">
        <id column="id" property="id" />
        <result column="userName" property="userName" />
    </resultMap>
</mapper>
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/common/persistence/model/User.java
New file
@@ -0,0 +1,51 @@
package com.stylefeng.guns.rest.common.persistence.model;
import com.baomidou.mybatisplus.activerecord.Model;
import java.io.Serializable;
/**
 * <p>
 *
 * </p>
 *
 * @author stylefeng
 * @since 2017-08-23
 */
public class User extends Model<User> {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String userName;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    @Override
    protected Serializable pkVal() {
        return this.id;
    }
    @Override
    public String toString() {
        return "User{" +
            "id=" + id +
            ", userName=" + userName +
            "}";
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/MessageConverConfig.java
New file
@@ -0,0 +1,28 @@
package com.stylefeng.guns.rest.config;
import com.stylefeng.guns.core.config.DefaultFastjsonConfig;
import com.stylefeng.guns.rest.config.properties.RestProperties;
import com.stylefeng.guns.rest.modular.auth.converter.WithSignMessageConverter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 签名校验messageConverter
 *
 * @author fengshuonan
 * @date 2017-08-25 16:04
 */
@Configuration
public class MessageConverConfig {
    @Bean
    @ConditionalOnProperty(prefix = RestProperties.REST_PREFIX, name = "sign-open", havingValue = "true", matchIfMissing = true)
    public WithSignMessageConverter withSignMessageConverter() {
        WithSignMessageConverter withSignMessageConverter = new WithSignMessageConverter();
        DefaultFastjsonConfig defaultFastjsonConfig = new DefaultFastjsonConfig();
        withSignMessageConverter.setFastJsonConfig(defaultFastjsonConfig.fastjsonConfig());
        withSignMessageConverter.setSupportedMediaTypes(defaultFastjsonConfig.getSupportedMediaType());
        return withSignMessageConverter;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/MybatisPlusConfig.java
New file
@@ -0,0 +1,25 @@
package com.stylefeng.guns.rest.config;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * MybatisPlus配置
 *
 * @author stylefeng
 * @Date 2017年8月23日12:51:41
 */
@Configuration
@MapperScan(basePackages = {"com.stylefeng.guns.rest.*.dao", "com.stylefeng.guns.rest.common.persistence.dao"})
public class MybatisPlusConfig {
    /**
     * mybatis-plus分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/WebConfig.java
New file
@@ -0,0 +1,30 @@
package com.stylefeng.guns.rest.config;
import com.stylefeng.guns.rest.config.properties.RestProperties;
import com.stylefeng.guns.rest.modular.auth.filter.AuthFilter;
import com.stylefeng.guns.rest.modular.auth.security.DataSecurityAction;
import com.stylefeng.guns.rest.modular.auth.security.impl.Base64SecurityAction;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * web配置
 *
 * @author fengshuonan
 * @date 2017-08-23 15:48
 */
@Configuration
public class WebConfig {
    @Bean
    @ConditionalOnProperty(prefix = RestProperties.REST_PREFIX, name = "auth-open", havingValue = "true", matchIfMissing = true)
    public AuthFilter jwtAuthenticationTokenFilter() {
        return new AuthFilter();
    }
    @Bean
    public DataSecurityAction dataSecurityAction() {
        return new Base64SecurityAction();
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/properties/JwtProperties.java
New file
@@ -0,0 +1,71 @@
package com.stylefeng.guns.rest.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * jwt相关配置
 *
 * @author fengshuonan
 * @date 2017-08-23 9:23
 */
@Configuration
@ConfigurationProperties(prefix = JwtProperties.JWT_PREFIX)
public class JwtProperties {
    public static final String JWT_PREFIX = "jwt";
    private String header = "Authorization";
    private String secret = "defaultSecret";
    private Long expiration = 604800L;
    private String authPath = "auth";
    private String md5Key = "randomKey";
    public static String getJwtPrefix() {
        return JWT_PREFIX;
    }
    public String getHeader() {
        return header;
    }
    public void setHeader(String header) {
        this.header = header;
    }
    public String getSecret() {
        return secret;
    }
    public void setSecret(String secret) {
        this.secret = secret;
    }
    public Long getExpiration() {
        return expiration;
    }
    public void setExpiration(Long expiration) {
        this.expiration = expiration;
    }
    public String getAuthPath() {
        return authPath;
    }
    public void setAuthPath(String authPath) {
        this.authPath = authPath;
    }
    public String getMd5Key() {
        return md5Key;
    }
    public void setMd5Key(String md5Key) {
        this.md5Key = md5Key;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/config/properties/RestProperties.java
New file
@@ -0,0 +1,37 @@
package com.stylefeng.guns.rest.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * 项目相关配置
 *
 * @author fengshuonan
 * @date 2017年10月23日16:44:15
 */
@Configuration
@ConfigurationProperties(prefix = RestProperties.REST_PREFIX)
public class RestProperties {
    public static final String REST_PREFIX = "rest";
    private boolean authOpen = true;
    private boolean signOpen = true;
    public boolean isAuthOpen() {
        return authOpen;
    }
    public void setAuthOpen(boolean authOpen) {
        this.authOpen = authOpen;
    }
    public boolean isSignOpen() {
        return signOpen;
    }
    public void setSignOpen(boolean signOpen) {
        this.signOpen = signOpen;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/controller/AuthController.java
New file
@@ -0,0 +1,44 @@
package com.stylefeng.guns.rest.modular.auth.controller;
import com.stylefeng.guns.core.exception.GunsException;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.modular.auth.controller.dto.AuthRequest;
import com.stylefeng.guns.rest.modular.auth.controller.dto.AuthResponse;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * 请求验证的
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:22
 */
@RestController
public class AuthController {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Resource(name = "simpleValidator")
    private IReqValidator reqValidator;
    @RequestMapping(value = "${jwt.auth-path}")
    public ResponseEntity<?> createAuthenticationToken(AuthRequest authRequest) {
        boolean validate = reqValidator.validate(authRequest);
        if (validate) {
            final String randomKey = jwtTokenUtil.getRandomKey();
            final String token = jwtTokenUtil.generateToken(authRequest.getUserName(), randomKey);
            return ResponseEntity.ok(new AuthResponse(token, randomKey));
        } else {
            throw new GunsException(BizExceptionEnum.AUTH_REQUEST_ERROR);
        }
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/controller/dto/AuthRequest.java
New file
@@ -0,0 +1,41 @@
package com.stylefeng.guns.rest.modular.auth.controller.dto;
import com.stylefeng.guns.rest.modular.auth.validator.dto.Credence;
/**
 * 认证的请求dto
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:00
 */
public class AuthRequest implements Credence {
    private String userName;
    private String password;
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getUserName() {
        return userName;
    }
    @Override
    public String getCredenceName() {
        return this.userName;
    }
    @Override
    public String getCredenceCode() {
        return this.password;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/controller/dto/AuthResponse.java
New file
@@ -0,0 +1,37 @@
package com.stylefeng.guns.rest.modular.auth.controller.dto;
import java.io.Serializable;
/**
 * 认证的响应结果
 *
 * @author fengshuonan
 * @Date 2017/8/24 13:58
 */
public class AuthResponse implements Serializable {
    private static final long serialVersionUID = 1250166508152483573L;
    /**
     * jwt token
     */
    private final String token;
    /**
     * 用于客户端混淆md5加密
     */
    private final String randomKey;
    public AuthResponse(String token, String randomKey) {
        this.token = token;
        this.randomKey = randomKey;
    }
    public String getToken() {
        return this.token;
    }
    public String getRandomKey() {
        return randomKey;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/converter/BaseTransferEntity.java
New file
@@ -0,0 +1,30 @@
package com.stylefeng.guns.rest.modular.auth.converter;
/**
 * 基础的传输bean
 *
 * @author fengshuonan
 * @date 2017-08-25 15:52
 */
public class BaseTransferEntity {
    private String object; //base64编码的json字符串
    private String sign;   //签名
    public String getObject() {
        return object;
    }
    public void setObject(String object) {
        this.object = object;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/converter/WithSignMessageConverter.java
New file
@@ -0,0 +1,64 @@
package com.stylefeng.guns.rest.modular.auth.converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.stylefeng.guns.core.exception.GunsException;
import com.stylefeng.guns.core.support.HttpKit;
import com.stylefeng.guns.core.util.MD5Util;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.config.properties.JwtProperties;
import com.stylefeng.guns.rest.modular.auth.security.DataSecurityAction;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
/**
 * 带签名的http信息转化器
 *
 * @author fengshuonan
 * @date 2017-08-25 15:42
 */
public class WithSignMessageConverter extends FastJsonHttpMessageConverter {
    @Autowired
    JwtProperties jwtProperties;
    @Autowired
    JwtTokenUtil jwtTokenUtil;
    @Autowired
    DataSecurityAction dataSecurityAction;
    @Override
    public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        InputStream in = inputMessage.getBody();
        Object o = JSON.parseObject(in, super.getFastJsonConfig().getCharset(), BaseTransferEntity.class, super.getFastJsonConfig().getFeatures());
        //先转化成原始的对象
        BaseTransferEntity baseTransferEntity = (BaseTransferEntity) o;
        //校验签名
        String token = HttpKit.getRequest().getHeader(jwtProperties.getHeader()).substring(7);
        String md5KeyFromToken = jwtTokenUtil.getMd5KeyFromToken(token);
        String object = baseTransferEntity.getObject();
        String json = dataSecurityAction.unlock(object);
        String encrypt = MD5Util.encrypt(object + md5KeyFromToken);
        if (encrypt.equals(baseTransferEntity.getSign())) {
            System.out.println("签名校验成功!");
        } else {
            System.out.println("签名校验失败,数据被改动过!");
            throw new GunsException(BizExceptionEnum.SIGN_ERROR);
        }
        //校验签名后再转化成应该的对象
        return JSON.parseObject(json, type);
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/filter/AuthFilter.java
New file
@@ -0,0 +1,66 @@
package com.stylefeng.guns.rest.modular.auth.filter;
import com.stylefeng.guns.core.base.tips.ErrorTip;
import com.stylefeng.guns.core.util.RenderUtil;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.config.properties.JwtProperties;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import io.jsonwebtoken.JwtException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 对客户端请求的jwt token验证过滤器
 *
 * @author fengshuonan
 * @Date 2017/8/24 14:04
 */
public class AuthFilter extends OncePerRequestFilter {
    private final Log logger = LogFactory.getLog(this.getClass());
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private JwtProperties jwtProperties;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request.getServletPath().equals("/" + jwtProperties.getAuthPath())) {
            chain.doFilter(request, response);
            return;
        }
        final String requestHeader = request.getHeader(jwtProperties.getHeader());
        String authToken = null;
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);
            //验证token是否过期,包含了验证jwt是否正确
            try {
                boolean flag = jwtTokenUtil.isTokenExpired(authToken);
                if (flag) {
                    RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
                    return;
                }
            } catch (JwtException e) {
                //有异常就是token解析失败
                RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
                return;
            }
        } else {
            //header没有带Bearer字段
            RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return;
        }
        chain.doFilter(request, response);
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/security/DataSecurityAction.java
New file
@@ -0,0 +1,31 @@
package com.stylefeng.guns.rest.modular.auth.security;
/**
 * <pre>
 * 信息传递的保护措施(传递的数据为json)
 *
 * 说明:
 * 可以根据实际开发时的需要,编写自己的数据加密方案,只需实现此类,并在WebConfig下配置您所编写的实现类即可
 * </pre>
 *
 * @author fengshuonan
 * @date 2017-09-18 20:41
 */
public interface DataSecurityAction {
    /**
     * 执行数据的保护措施
     *
     * @author stylefeng
     * @Date 2017/9/18 20:42
     */
    String doAction(String beProtected);
    /**
     * 解除保护
     *
     * @author stylefeng
     * @Date 2017/9/18 20:45
     */
    String unlock(String securityCode);
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/security/impl/Base64SecurityAction.java
New file
@@ -0,0 +1,24 @@
package com.stylefeng.guns.rest.modular.auth.security.impl;
import com.stylefeng.guns.rest.modular.auth.security.DataSecurityAction;
import org.springframework.util.Base64Utils;
/**
 * 对数据进行base64编码的方式
 *
 * @author fengshuonan
 * @date 2017-09-18 20:43
 */
public class Base64SecurityAction implements DataSecurityAction {
    @Override
    public String doAction(String beProtected) {
        return Base64Utils.encodeToString(beProtected.getBytes());
    }
    @Override
    public String unlock(String securityCode) {
        byte[] bytes = Base64Utils.decodeFromString(securityCode);
        return new String(bytes);
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/util/JwtTokenUtil.java
New file
@@ -0,0 +1,140 @@
package com.stylefeng.guns.rest.modular.auth.util;
import com.stylefeng.guns.core.util.ToolUtil;
import com.stylefeng.guns.rest.config.properties.JwtProperties;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * <p>jwt token工具类</p>
 * <pre>
 *     jwt的claim里一般包含以下几种数据:
 *         1. iss -- token的发行者
 *         2. sub -- 该JWT所面向的用户
 *         3. aud -- 接收该JWT的一方
 *         4. exp -- token的失效时间
 *         5. nbf -- 在此时间段之前,不会被处理
 *         6. iat -- jwt发布时间
 *         7. jti -- jwt唯一标识,防止重复使用
 * </pre>
 *
 * @author fengshuonan
 * @Date 2017/8/25 10:59
 */
@Component
public class JwtTokenUtil {
    @Autowired
    private JwtProperties jwtProperties;
    /**
     * 获取用户名从token中
     */
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token).getSubject();
    }
    /**
     * 获取jwt发布时间
     */
    public Date getIssuedAtDateFromToken(String token) {
        return getClaimFromToken(token).getIssuedAt();
    }
    /**
     * 获取jwt失效时间
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token).getExpiration();
    }
    /**
     * 获取jwt接收者
     */
    public String getAudienceFromToken(String token) {
        return getClaimFromToken(token).getAudience();
    }
    /**
     * 获取私有的jwt claim
     */
    public String getPrivateClaimFromToken(String token, String key) {
        return getClaimFromToken(token).get(key).toString();
    }
    /**
     * 获取md5 key从token中
     */
    public String getMd5KeyFromToken(String token) {
        return getPrivateClaimFromToken(token, jwtProperties.getMd5Key());
    }
    /**
     * 获取jwt的payload部分
     */
    public Claims getClaimFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(jwtProperties.getSecret())
                .parseClaimsJws(token)
                .getBody();
    }
    /**
     * 解析token是否正确,不正确会报异常<br>
     */
    public void parseToken(String token) throws JwtException {
        Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody();
    }
    /**
     * <pre>
     *  验证token是否失效
     *  true:过期   false:没过期
     * </pre>
     */
    public Boolean isTokenExpired(String token) {
        try {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        } catch (ExpiredJwtException expiredJwtException) {
            return true;
        }
    }
    /**
     * 生成token(通过用户名和签名时候用的随机数)
     */
    public String generateToken(String userName, String randomKey) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(jwtProperties.getMd5Key(), randomKey);
        return doGenerateToken(claims, userName);
    }
    /**
     * 生成token
     */
    private String doGenerateToken(Map<String, Object> claims, String subject) {
        final Date createdDate = new Date();
        final Date expirationDate = new Date(createdDate.getTime() + jwtProperties.getExpiration() * 1000);
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(createdDate)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
                .compact();
    }
    /**
     * 获取混淆MD5签名用的随机字符串
     */
    public String getRandomKey() {
        return ToolUtil.getRandomString(6);
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/IReqValidator.java
New file
@@ -0,0 +1,22 @@
package com.stylefeng.guns.rest.modular.auth.validator;
import com.stylefeng.guns.rest.modular.auth.validator.dto.Credence;
/**
 * <p>验证请求/auth接口时,请求参数的正确性</p>
 * <p>
 * <p>如果想拓展验证方法只需实现这个接口,然后在AuthenticationRestController类中注意相应实现的本接口的类即可</p>
 *
 * @author fengshuonan
 * @date 2017-08-23 11:48
 */
public interface IReqValidator {
    /**
     * 通过请求参数验证
     *
     * @author fengshuonan
     * @Date 2017/8/23 11:49
     */
    boolean validate(Credence credence);
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/dto/Credence.java
New file
@@ -0,0 +1,20 @@
package com.stylefeng.guns.rest.modular.auth.validator.dto;
/**
 * 验证的凭据
 *
 * @author fengshuonan
 * @date 2017-08-27 13:27
 */
public interface Credence {
    /**
     * 凭据名称
     */
    String getCredenceName();
    /**
     * 密码或者是其他的验证码之类的
     */
    String getCredenceCode();
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/impl/DbValidator.java
New file
@@ -0,0 +1,35 @@
package com.stylefeng.guns.rest.modular.auth.validator.impl;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.stylefeng.guns.rest.common.persistence.dao.UserMapper;
import com.stylefeng.guns.rest.common.persistence.model.User;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import com.stylefeng.guns.rest.modular.auth.validator.dto.Credence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
 * 账号密码验证
 *
 * @author fengshuonan
 * @date 2017-08-23 12:34
 */
@Service
public class DbValidator implements IReqValidator {
    @Autowired
    UserMapper userMapper;
    @Override
    public boolean validate(Credence credence) {
        List<User> users = userMapper.selectList(new EntityWrapper<User>().eq("userName", credence.getCredenceName()));
        if (users != null && users.size() > 0) {
            return true;
        } else {
            return false;
        }
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/auth/validator/impl/SimpleValidator.java
New file
@@ -0,0 +1,32 @@
package com.stylefeng.guns.rest.modular.auth.validator.impl;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import com.stylefeng.guns.rest.modular.auth.validator.dto.Credence;
import org.springframework.stereotype.Service;
/**
 * 直接验证账号密码是不是admin
 *
 * @author fengshuonan
 * @date 2017-08-23 12:34
 */
@Service
public class SimpleValidator implements IReqValidator {
    private static String USER_NAME = "admin";
    private static String PASSWORD = "admin";
    @Override
    public boolean validate(Credence credence) {
        String userName = credence.getCredenceName();
        String password = credence.getCredenceCode();
        if (USER_NAME.equals(userName) && PASSWORD.equals(password)) {
            return true;
        } else {
            return false;
        }
    }
}
DispatchOKTravel/guns-rest/src/main/java/com/stylefeng/guns/rest/modular/example/ExampleController.java
New file
@@ -0,0 +1,24 @@
package com.stylefeng.guns.rest.modular.example;
import com.stylefeng.guns.rest.common.SimpleObject;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * 常规控制器
 *
 * @author fengshuonan
 * @date 2017-08-23 16:02
 */
@Controller
@RequestMapping("/hello")
public class ExampleController {
    @RequestMapping("")
    public ResponseEntity hello(@RequestBody SimpleObject simpleObject) {
        System.out.println(simpleObject.getUser());
        return ResponseEntity.ok("请求成功!");
    }
}
DispatchOKTravel/guns-rest/src/main/resources/application.yml
New file
@@ -0,0 +1,40 @@
rest:
  auth-open: true #jwt鉴权机制是否开启(true或者false)
  sign-open: true #签名机制是否开启(true或false)
jwt:
  header: Authorization   #http请求头所需要的字段
  secret: mySecret        #jwt秘钥
  expiration: 604800      #7天 单位:秒
  auth-path: auth         #认证请求的路径
  md5-key: randomKey      #md5加密混淆key
server:
  port: 8080 #项目端口
mybatis-plus:
  mapper-locations: classpath*:com/stylefeng/guns/rest/**/mapping/*.xml
  typeAliasesPackage: com.stylefeng.guns.rest.common.persistence.model
  global-config:
    id-type: 0  #0:数据库ID自增   1:用户输入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)
    db-column-underline: false
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: false
    cache-enabled: true #配置的缓存的全局开关
    lazyLoadingEnabled: true #延时加载的开关
    multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用
spring:
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/guns_rest?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=UTC
      username: root
      password: root
      filters: log4j,wall,mergeStat
logging:
  level.root: info
  level.com.stylefeng: debug
  path: logs/
  file: guns-rest.log
DispatchOKTravel/pom.xml
New file
@@ -0,0 +1,253 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         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>
    <groupId>com.stylefeng</groupId>
    <artifactId>guns-parent</artifactId>
    <version>1.0.0</version>
    <name>guns-parent</name>
    <packaging>pom</packaging>
    <modules>
        <module>guns-admin</module>
        <module>guns-core</module>
        <module>guns-rest</module>
        <module>guns-generator</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>
    <properties>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <guns.version>1.0.0</guns.version>
        <mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
        <shiro.version>1.4.0</shiro.version>
        <mybatis-plus.version>2.3</mybatis-plus.version>
        <fastjson.version>1.2.47</fastjson.version>
        <velocity.version>1.7</velocity.version>
        <kaptcha.version>2.3.2</kaptcha.version>
        <ehcache.version>3.3.1</ehcache.version>
        <zxing.version>3.2.1</zxing.version>
        <druid.version>1.1.10</druid.version>
        <beetl.version>2.8.5</beetl.version>
        <swagger.version>2.9.2</swagger.version>
        <commons-lang3.version>3.7</commons-lang3.version>
        <ehcache.core.version>2.6.11</ehcache.core.version>
        <mysql-connector-java.version>8.0.11</mysql-connector-java.version>
        <jwt.version>0.9.0</jwt.version>
        <hutool.version>4.1.1</hutool.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.stylefeng</groupId>
                <artifactId>guns-generator</artifactId>
                <version>${guns.version}</version>
            </dependency>
            <dependency>
                <groupId>com.stylefeng</groupId>
                <artifactId>guns-core</artifactId>
                <version>${guns.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.0.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatisplus-spring-boot-starter</artifactId>
                <version>${mybatisplus-spring-boot-starter.version}</version>
            </dependency>
            <!--shiro依赖-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql-connector-java.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-generate</artifactId>
                <version>${mybatis-plus.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>com.github.penggle</groupId>
                <artifactId>kaptcha</artifactId>
                <version>${kaptcha.version}</version>
            </dependency>
            <dependency>
                <groupId>org.ehcache</groupId>
                <artifactId>ehcache</artifactId>
                <version>${ehcache.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.zxing</groupId>
                <artifactId>core</artifactId>
                <version>${zxing.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.velocity</groupId>
                <artifactId>velocity</artifactId>
                <version>${velocity.version}</version>
            </dependency>
            <dependency>
                <groupId>com.ibeetl</groupId>
                <artifactId>beetl</artifactId>
                <version>${beetl.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${commons-lang3.version}</version>
            </dependency>
            <dependency>
                <groupId>net.sf.ehcache</groupId>
                <artifactId>ehcache-core</artifactId>
                <version>${ehcache.core.version}</version>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>${jwt.version}</version>
            </dependency>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <delimiters>
                        <delimiter>@</delimiter>
                    </delimiters>
                    <useDefaultDelimiters>false</useDefaultDelimiters>
                </configuration>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>
    <profiles>
        <profile>
            <id>local</id>
            <properties>
                <spring.active>local</spring.active>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
        </profile>
        <profile>
            <id>dev</id>
            <properties>
                <spring.active>dev</spring.active>
            </properties>
        </profile>
        <profile>
            <id>test</id>
            <properties>
                <spring.active>test</spring.active>
            </properties>
        </profile>
        <profile>
            <id>produce</id>
            <properties>
                <spring.active>produce</spring.active>
            </properties>
        </profile>
    </profiles>
</project>