Commit 9f43851e authored by zhengjie's avatar zhengjie
Browse files

1.7版本发布,详情查看版本说明

parent 1402e584
# eladmin
项目基于 Spring Boot 2.1.0 、 Spring boot Jpa、 Spring Security、redis、Vue的前后端分离的权限管理系统,项目采用分模块开发方式, 权限控制采用 RBAC(Role-Based Access Control,基于角色的访问控制),前端菜单支持动态路由
项目基于 Spring Boot 2.1.0 、 Spring boot Jpa、 Spring Security、redis、Vue的前后端分离的权限管理系统,项目采用分模块开发方式, 权限控制采用 RBAC(Role-Based Access Control,基于角色的访问控制),支持数据字典、数据权限管理、前端菜单支持动态路由
#### 前端源码
eladmin-qt和eladmin-qd只是命名方式的区别,无其他区别
- 码云:[https://gitee.com/elunez/eladmin-qt](https://gitee.com/elunez/eladmin-qt)
- github:[https://github.com/elunez/eladmin-qd](https://github.com/elunez/eladmin-qd)
#### 项目源码
#### eladmin开发文档
[http://docs.auauz.net/#/](http://docs.auauz.net/#/)
| | 后端源码 | 前端源码 |
|--- |--- | --- |
| github | https://github.com/elunez/eladmin | https://github.com/elunez/eladmin-qd |
| 码云 | https://github.com/elunez/eladmin | https://gitee.com/elunez/eladmin-qt |
#### 开发文档
[https://docs.auauz.net/#/](https://docs.auauz.net/#/)
#### 预览地址
[http://auauz.net](http://auauz.net)
[https://auauz.net](https://auauz.net)
##### 用户账号密码
......@@ -23,7 +24,7 @@ eladmin-qt和eladmin-qd只是命名方式的区别,无其他区别
- JDK:8
- IDE:IntelliJ IDEA (后端)
- IDE:JetBrains WebStorm (前端)
- IDE:JetBrains WebStorm(前端)
- 依赖管理:Maven
- 数据库:MySQL 5.5.59
......@@ -31,19 +32,21 @@ eladmin-qt和eladmin-qd只是命名方式的区别,无其他区别
```
- 系统管理
- 用户管理 提供用户的相关配置
- 角色管理 角色菜单进行权限的分配
- Swagger文档 localhost:8000/swagger-ui.html
- 角色管理 对权限与菜单进行分配
- 权限管理 权限细化到接口
- 菜单管理 已实现菜单动态路由,后端可配置化,支持多级菜单
- 定时任务 整合Quartz做定时任务,加入任务日志,任务运行情况一目了然
- 代码生成 高灵活度一键生成前后端代码,减少百分之80左右的工作任务
- 部门管理与岗位管理
- 字典管理 应广大码友的要求加入字典管理
- 系统监控
- 操作日志 使用apo记录用户操作日志
- 异常日志 记录操作过程中的异常,并且提供查看异常的堆栈信息
- 系统缓存 使用jedis将缓存操作可视化,并提供对redis的基本操作,可根据需求自行扩展
- 实时控制台 实时打印logback日志,来自微强迫症患者的精心配色,更好的监控系统的运行状态
- SQL监控 采用druid 监控数据库访问性能,默认用户名admin,密码123456
- 三方工具
- 系统工具
- 定时任务 整合Quartz做定时任务,加入任务日志,任务运行情况一目了然
- 代码生成 高灵活度一键生成前后端代码,减少百分之80左右的工作任务
- 接口文档 使用的是 swagger-ui
- 邮件工具 配合富文本,发送html格式的邮件
- SM.MS免费图床 挺好用的一个图床,作为公共图片上传使用
- 七牛云存储 这个就不多说了
......@@ -51,6 +54,7 @@ eladmin-qt和eladmin-qd只是命名方式的区别,无其他区别
- 组件管理
- 图标库 系统图标来自 https://www.iconfont.cn/
- 富文本 集成wangEditor富文本
- Markdown编辑器与Yaml编辑器
```
#### 项目结构
```
......@@ -133,8 +137,7 @@ eladmin-qt和eladmin-qd只是命名方式的区别,无其他区别
| 微信 | 支付宝 |
|--- | --- |
| ![](https://i.imgur.com/QJ2pqyg.png) | ![](https://i.imgur.com/eO95P7Q.png) |
| ![](https://i.loli.net/2019/03/28/5c9c951c61a9a.png) | ![](https://i.loli.net/2019/03/28/5c9c95355fecb.png) |
#### 反馈交流
- QQ交流群:891137268
......@@ -19,7 +19,6 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
......@@ -31,6 +30,7 @@ import java.time.Duration;
@Slf4j
@Configuration
@EnableCaching
// 自动配置
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig extends CachingConfigurerSupport {
......@@ -91,6 +91,7 @@ public class RedisConfig extends CachingConfigurerSupport {
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 全局开启AutoType,不建议使用
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
......
......@@ -47,7 +47,7 @@ public class SwaggerConfig {
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("elune 接口文档")
.version("1.5")
.version("1.7")
.build();
}
......
......@@ -95,6 +95,6 @@ public class EncryptUtils {
}
public static void main(String[] args) {
System.out.println(encryptPassword("e10adc3949ba59abbe56e057f20f883e"));
System.out.println(encryptPassword("123456"));
}
}
package me.zhengjie.utils;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
/**
* @author jie
* @date 2019-01-08
*/
@Configuration
public class ThreadPoolUtil {
@Bean
public ExecutorService getThreadPool(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
int size = 2;
ExecutorService executorService = new ThreadPoolExecutor(size,size,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),namedThreadFactory);
return executorService;
}
}
......@@ -21,7 +21,7 @@ import org.springframework.stereotype.Component;
@Component
@Aspect
@Slf4j
public class LogAspect {
public class DataScopeAspect {
@Autowired
private LogService logService;
......
......@@ -2,6 +2,7 @@ package me.zhengjie.rest;
import me.zhengjie.domain.Log;
import me.zhengjie.service.query.LogQueryService;
import me.zhengjie.utils.SecurityContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
......@@ -29,6 +30,13 @@ public class LogController {
return new ResponseEntity(logQueryService.queryAll(log,pageable), HttpStatus.OK);
}
@GetMapping(value = "/logs/user")
public ResponseEntity getUserLogs(Log log, Pageable pageable){
log.setLogType("INFO");
log.setUsername(SecurityContextHolder.getUserDetails().getUsername());
return new ResponseEntity(logQueryService.queryAll(log,pageable), HttpStatus.OK);
}
@GetMapping(value = "/logs/error")
@PreAuthorize("hasAnyRole('ADMIN')")
public ResponseEntity getErrorLogs(Log log, Pageable pageable){
......
......@@ -10,7 +10,6 @@ import me.zhengjie.utils.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
......@@ -29,9 +28,6 @@ public class LogServiceImpl implements LogService {
@Autowired
private LogRepository logRepository;
@Value("${jwt.header}")
private String tokenHeader;
private final String LOGINPATH = "login";
@Override
......
This diff is collapsed.
package me.zhengjie.config;
import me.zhengjie.modules.system.domain.Dept;
import me.zhengjie.modules.system.domain.Role;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.service.DeptService;
import me.zhengjie.modules.system.service.RoleService;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.utils.SecurityContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 数据权限配置
* @author jie
* @date 2019-4-1
*/
@Component
public class DataScope {
private final String[] scopeType = {"全部","本级","自定义"};
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private DeptService deptService;
public Set<Long> getDeptIds() {
User user = userService.findByName(SecurityContextHolder.getUserDetails().getUsername());
// 用于存储部门id
Set<Long> deptIds = new HashSet<>();
// 查询用户角色
List<Role> roleSet = roleService.findByUsers_Id(user.getId());
for (Role role : roleSet) {
if (scopeType[0].equals(role.getDataScope())) {
return new HashSet<>() ;
}
// 存储本级的数据权限
if (scopeType[1].equals(role.getDataScope())) {
deptIds.add(user.getDept().getId());
}
// 存储自定义的数据权限
if (scopeType[2].equals(role.getDataScope())) {
Set<Dept> deptList = role.getDepts();
for (Dept dept : deptList) {
deptIds.add(dept.getId());
List<Dept> deptChildren = deptService.findByPid(dept.getId());
if (deptChildren != null && deptChildren.size() != 0) {
deptIds.addAll(getDeptChildren(deptChildren));
}
}
}
}
return deptIds;
}
public List<Long> getDeptChildren(List<Dept> deptList) {
List<Long> list = new ArrayList<>();
deptList.forEach(dept -> {
if (dept!=null && dept.getEnabled()){
List<Dept> depts = deptService.findByPid(dept.getId());
if(deptList!=null && deptList.size()!=0){
list.addAll(getDeptChildren(depts));
}
list.add(dept.getId());
}
}
);
return list;
}
}
......@@ -11,14 +11,13 @@ import java.util.Date;
/**
* 定义Logfilter拦截输出日志
* @author jie
* @reference https://cloud.tencent.com/developer/article/1096792
* @date 2018-12-24
*/
public class LogFilter extends Filter<ILoggingEvent>{
@Override
public FilterReply decide(ILoggingEvent event) {
String exception = "";
IThrowableProxy iThrowableProxy1 = event.getThrowableProxy();
LogMessage loggerMessage = new LogMessage(
event.getFormattedMessage(),
DateFormat.getDateTimeInstance().format(new Date(event.getTimeStamp())),
......
......@@ -6,7 +6,7 @@ import java.util.concurrent.LinkedBlockingQueue;
/**
* 创建一个阻塞队列,作为日志系统输出的日志的一个临时载体
* @author jie
* @author https://cloud.tencent.com/developer/article/1096792
* @date 2018-12-24
*/
public class LoggerQueue {
......
package me.zhengjie.modules.monitor.config;
import me.zhengjie.modules.monitor.service.VisitsService;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* 初始化站点统计
* @author jie
*/
@Configuration
public class VisitsInitialization {
@Component
public class VisitsInitialization implements ApplicationRunner {
public VisitsInitialization(VisitsService visitsService){
@Autowired
private VisitsService visitsService;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("--------------- 初始化站点统计,如果存在今日统计则跳过 ---------------");
visitsService.save();
System.out.println("--------------- 初始化站点统计完成 ---------------");
......
......@@ -9,10 +9,12 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 配置WebSocket消息代理端点,即stomp服务端
* @author jie
* @reference https://cloud.tencent.com/developer/article/1096792
* @date 2018-12-24
*/
@Slf4j
......@@ -22,8 +24,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Autowired
private ExecutorService executorService;
private ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
......
......@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author 郑杰
* @author https://cloud.tencent.com/developer/article/1096792
* @date 2018-12-24
*/
@Data
......
......@@ -82,7 +82,7 @@ public class RedisServiceImpl implements RedisService {
Jedis jedis = null;
try{
jedis = pool.getResource();
jedis.flushDB();
jedis.flushAll();
}finally{
if(null != jedis){
jedis.close(); // 释放资源还给连接池
......
......@@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.List;
/**
......
......@@ -7,14 +7,12 @@ import me.zhengjie.modules.quartz.service.QuartzJobService;
import me.zhengjie.utils.SpringContextHolder;
import me.zhengjie.utils.ThrowableUtil;
import org.quartz.JobExecutionContext;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
......@@ -25,13 +23,9 @@ import java.util.concurrent.Future;
@Async
public class ExecutionJob extends QuartzJobBean {
@Resource(name = "scheduler")
private Scheduler scheduler;
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ExecutorService executorService;
private ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
protected void executeInternal(JobExecutionContext context) {
......
......@@ -110,9 +110,8 @@ public class QuartzManage {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null){
if(trigger == null)
addJob(quartzJob);
}
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.resumeJob(jobKey);
} catch (Exception e){
......@@ -131,9 +130,8 @@ public class QuartzManage {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if(trigger == null){
if(trigger == null)
addJob(quartzJob);
}
JobDataMap dataMap = new JobDataMap();
dataMap.put(QuartzJob.JOB_KEY, quartzJob);
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
......
......@@ -93,12 +93,13 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers( HttpMethod.POST,"/auth/"+loginPath).permitAll()
.antMatchers("/websocket/**").permitAll()
.antMatchers("/druid/**").anonymous()
// 支付宝回调
.antMatchers("/api/aliPay/return").anonymous()
.antMatchers("/api/aliPay/notify").anonymous()
// 系统监控
.antMatchers("/actuator/**").anonymous()
// swagger start
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
......@@ -110,8 +111,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers("/test/**").anonymous()
.antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
.antMatchers("/druid/**").permitAll()
// 所有请求都需要认证
.anyRequest().authenticated();
.anyRequest().authenticated()
// 防止iframe 造成跨域
.and().headers().frameOptions().disable();
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment