mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 08:13:44 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/dev' into satoken
# Conflicts: # pom.xml # ruoyi-framework/src/main/java/com/ruoyi/framework/config/AsyncConfig.java # ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java # ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java # ruoyi-system/src/main/java/com/ruoyi/system/service/SysLoginService.java
This commit is contained in:
		@@ -14,6 +14,8 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼
 | 
			
		||||
| 功能介绍 | 使用技术 | 文档地址 | 特性注意事项 |
 | 
			
		||||
|---|---|---|---|
 | 
			
		||||
| 当前框架 | RuoYi-Vue-Plus | [RuoYi-Vue-Plus文档](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages) | 重写RuoYi-Vue全方位升级(不兼容原框架) |
 | 
			
		||||
| satoken分支 | RuoYi-Vue-Plus-satoken | [satoken分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/) | 使用satoken重构权限鉴权(仅供学习不推荐上生产) |
 | 
			
		||||
| 单体分支 | RuoYi-Vue-Plus-fast | [fast分支地址](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/) | 单体应用结构 |
 | 
			
		||||
| 原框架 | RuoYi-Vue | [RuoYi-Vue官网](http://ruoyi.vip/) | 定期同步需要的功能 |
 | 
			
		||||
| 前端开发框架 | Vue、Element UI | [Element UI官网](https://element.eleme.cn/#/zh-CN) | |
 | 
			
		||||
| 后端开发框架 | SpringBoot | [SpringBoot官网](https://spring.io/projects/spring-boot/#learn) | |
 | 
			
		||||
@@ -69,6 +71,7 @@ RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 `分布式集群` 场景升级(不兼
 | 
			
		||||
* 同步升级 RuoYi-Vue
 | 
			
		||||
* GitHub 地址 [RuoYi-Vue-Plus-github](https://github.com/JavaLionLi/RuoYi-Vue-Plus)
 | 
			
		||||
* 单模块 fast 分支 [RuoYi-Vue-Plus-fast](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/fast/)
 | 
			
		||||
* satoken 分支 [RuoYi-Vue-Plus-satoken](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/tree/satoken/)
 | 
			
		||||
* 用户扩展项目 [扩展项目列表](https://gitee.com/JavaLionLi/RuoYi-Vue-Plus/wikis/pages?sort_id=4478302&doc_id=1469725)
 | 
			
		||||
 | 
			
		||||
## 加群与捐献
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								pom.xml
									
									
									
									
									
								
							@@ -14,7 +14,7 @@
 | 
			
		||||
 | 
			
		||||
    <properties>
 | 
			
		||||
        <ruoyi-vue-plus.version>3.1.0</ruoyi-vue-plus.version>
 | 
			
		||||
        <spring-boot.version>2.5.4</spring-boot.version>
 | 
			
		||||
        <spring-boot.version>2.5.5</spring-boot.version>
 | 
			
		||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
			
		||||
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 | 
			
		||||
        <java.version>1.8</java.version>
 | 
			
		||||
@@ -26,14 +26,14 @@
 | 
			
		||||
        <easyexcel.version>2.2.11</easyexcel.version>
 | 
			
		||||
        <velocity.version>1.7</velocity.version>
 | 
			
		||||
        <satoken.version>1.26.0</satoken.version>
 | 
			
		||||
        <mybatis-plus.version>3.4.3.3</mybatis-plus.version>
 | 
			
		||||
        <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
 | 
			
		||||
        <p6spy.version>3.9.1</p6spy.version>
 | 
			
		||||
        <hutool.version>5.7.13</hutool.version>
 | 
			
		||||
        <feign.version>3.0.3</feign.version>
 | 
			
		||||
        <feign-okhttp.version>11.6</feign-okhttp.version>
 | 
			
		||||
        <okhttp.version>4.9.1</okhttp.version>
 | 
			
		||||
        <spring-boot-admin.version>2.5.1</spring-boot-admin.version>
 | 
			
		||||
        <redisson.version>3.16.2</redisson.version>
 | 
			
		||||
        <redisson.version>3.16.3</redisson.version>
 | 
			
		||||
        <lock4j.version>2.2.1</lock4j.version>
 | 
			
		||||
        <dynamic-ds.version>3.4.1</dynamic-ds.version>
 | 
			
		||||
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
        <!-- OSS 配置 -->
 | 
			
		||||
        <qiniu.version>7.8.0</qiniu.version>
 | 
			
		||||
        <aliyun.oss.version>3.13.1</aliyun.oss.version>
 | 
			
		||||
        <qcloud.cos.version>5.6.51</qcloud.cos.version>
 | 
			
		||||
        <qcloud.cos.version>5.6.55</qcloud.cos.version>
 | 
			
		||||
        <minio.version>8.3.0</minio.version>
 | 
			
		||||
 | 
			
		||||
        <!-- docker 配置 -->
 | 
			
		||||
 
 | 
			
		||||
@@ -122,6 +122,28 @@ sa-token:
 | 
			
		||||
  # 是否输出操作日志
 | 
			
		||||
  is-log: true
 | 
			
		||||
 | 
			
		||||
# security配置
 | 
			
		||||
security:
 | 
			
		||||
  # 登出路径
 | 
			
		||||
  logout-url: /logout
 | 
			
		||||
  # 匿名路径
 | 
			
		||||
  anonymous:
 | 
			
		||||
    - /login
 | 
			
		||||
    - /register
 | 
			
		||||
    - /captchaImage
 | 
			
		||||
    # swagger 文档配置
 | 
			
		||||
    - /doc.html
 | 
			
		||||
    - /swagger-resources/**
 | 
			
		||||
    - /webjars/**
 | 
			
		||||
    - /*/api-docs
 | 
			
		||||
    # druid 监控配置
 | 
			
		||||
    - /druid/**
 | 
			
		||||
    # actuator 监控配置
 | 
			
		||||
    - /actuator
 | 
			
		||||
    - /actuator/**
 | 
			
		||||
  # 用户放行
 | 
			
		||||
  permit-all:
 | 
			
		||||
 | 
			
		||||
# 重复提交
 | 
			
		||||
repeat-submit:
 | 
			
		||||
  # 全局间隔时间(毫秒)
 | 
			
		||||
@@ -237,6 +259,11 @@ swagger:
 | 
			
		||||
    name: Lion Li
 | 
			
		||||
    email: crazylionli@163.com
 | 
			
		||||
    url: https://gitee.com/JavaLionLi/RuoYi-Vue-Plus
 | 
			
		||||
  groups:
 | 
			
		||||
    - name: 演示案例
 | 
			
		||||
      basePackage: com.ruoyi.demo
 | 
			
		||||
    - name: 系统模块
 | 
			
		||||
      basePackage: com.ruoyi.admin
 | 
			
		||||
 | 
			
		||||
# 防止XSS攻击
 | 
			
		||||
xss:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,260 +0,0 @@
 | 
			
		||||
package com.ruoyi.common.core.redis;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.Lists;
 | 
			
		||||
import org.redisson.api.*;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * spring redis 工具类
 | 
			
		||||
 *
 | 
			
		||||
 * @author shenxinquan
 | 
			
		||||
 * @see com.ruoyi.common.utils.RedisUtils
 | 
			
		||||
 * @deprecated 3.2.0 删除此类
 | 
			
		||||
 **/
 | 
			
		||||
@SuppressWarnings(value = {"unchecked", "rawtypes"})
 | 
			
		||||
@Component
 | 
			
		||||
@Deprecated
 | 
			
		||||
public class RedisCache {
 | 
			
		||||
 | 
			
		||||
	@Autowired
 | 
			
		||||
	private RedissonClient redissonClient;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 发布通道消息
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param channelKey 通道key
 | 
			
		||||
	 * @param msg 发送数据
 | 
			
		||||
	 * @param consumer 自定义处理
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> void publish(String channelKey, T msg, Consumer<T> consumer) {
 | 
			
		||||
		RTopic topic = redissonClient.getTopic(channelKey);
 | 
			
		||||
		topic.publish(msg);
 | 
			
		||||
		consumer.accept(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public <T> void publish(String channelKey, T msg) {
 | 
			
		||||
		RTopic topic = redissonClient.getTopic(channelKey);
 | 
			
		||||
		topic.publish(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 订阅通道接收消息
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param channelKey 通道key
 | 
			
		||||
	 * @param clazz 消息类型
 | 
			
		||||
	 * @param consumer 自定义处理
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> void subscribe(String channelKey, Class<T> clazz, Consumer<T> consumer) {
 | 
			
		||||
		RTopic topic = redissonClient.getTopic(channelKey);
 | 
			
		||||
		topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 缓存基本的对象,Integer、String、实体类等
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key   缓存的键值
 | 
			
		||||
	 * @param value 缓存的值
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> void setCacheObject(final String key, final T value) {
 | 
			
		||||
		redissonClient.getBucket(key).set(value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 缓存基本的对象,Integer、String、实体类等
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key      缓存的键值
 | 
			
		||||
	 * @param value    缓存的值
 | 
			
		||||
	 * @param timeout  时间
 | 
			
		||||
	 * @param timeUnit 时间颗粒度
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
 | 
			
		||||
		RBucket<T> result = redissonClient.getBucket(key);
 | 
			
		||||
		result.set(value);
 | 
			
		||||
		result.expire(timeout, timeUnit);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 设置有效时间
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key     Redis键
 | 
			
		||||
	 * @param timeout 超时时间
 | 
			
		||||
	 * @return true=设置成功;false=设置失败
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean expire(final String key, final long timeout) {
 | 
			
		||||
		return expire(key, timeout, TimeUnit.SECONDS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 设置有效时间
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key     Redis键
 | 
			
		||||
	 * @param timeout 超时时间
 | 
			
		||||
	 * @param unit    时间单位
 | 
			
		||||
	 * @return true=设置成功;false=设置失败
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean expire(final String key, final long timeout, final TimeUnit unit) {
 | 
			
		||||
		RBucket rBucket = redissonClient.getBucket(key);
 | 
			
		||||
		return rBucket.expire(timeout, unit);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获得缓存的基本对象。
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key 缓存键值
 | 
			
		||||
	 * @return 缓存键值对应的数据
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> T getCacheObject(final String key) {
 | 
			
		||||
		RBucket<T> rBucket = redissonClient.getBucket(key);
 | 
			
		||||
		return rBucket.get();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 删除单个对象
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key
 | 
			
		||||
	 */
 | 
			
		||||
	public boolean deleteObject(final String key) {
 | 
			
		||||
		return redissonClient.getBucket(key).delete();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* */
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 删除集合对象
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param collection 多个对象
 | 
			
		||||
	 * @return
 | 
			
		||||
	 */
 | 
			
		||||
	public void deleteObject(final Collection collection) {
 | 
			
		||||
		RBatch batch = redissonClient.createBatch();
 | 
			
		||||
		collection.forEach(t->{
 | 
			
		||||
			batch.getBucket(t.toString()).deleteAsync();
 | 
			
		||||
		});
 | 
			
		||||
		batch.execute();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 缓存List数据
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key      缓存的键值
 | 
			
		||||
	 * @param dataList 待缓存的List数据
 | 
			
		||||
	 * @return 缓存的对象
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> boolean setCacheList(final String key, final List<T> dataList) {
 | 
			
		||||
		RList<T> rList = redissonClient.getList(key);
 | 
			
		||||
		return rList.addAll(dataList);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获得缓存的list对象
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key 缓存的键值
 | 
			
		||||
	 * @return 缓存键值对应的数据
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> List<T> getCacheList(final String key) {
 | 
			
		||||
		RList<T> rList = redissonClient.getList(key);
 | 
			
		||||
		return rList.readAll();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 缓存Set
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key     缓存键值
 | 
			
		||||
	 * @param dataSet 缓存的数据
 | 
			
		||||
	 * @return 缓存数据的对象
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> boolean setCacheSet(final String key, final Set<T> dataSet) {
 | 
			
		||||
		RSet<T> rSet = redissonClient.getSet(key);
 | 
			
		||||
		return rSet.addAll(dataSet);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获得缓存的set
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key
 | 
			
		||||
	 * @return
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> Set<T> getCacheSet(final String key) {
 | 
			
		||||
		RSet<T> rSet = redissonClient.getSet(key);
 | 
			
		||||
		return rSet.readAll();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 缓存Map
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key
 | 
			
		||||
	 * @param dataMap
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
 | 
			
		||||
		if (dataMap != null) {
 | 
			
		||||
			RMap<String, T> rMap = redissonClient.getMap(key);
 | 
			
		||||
			rMap.putAll(dataMap);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获得缓存的Map
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key
 | 
			
		||||
	 * @return
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> Map<String, T> getCacheMap(final String key) {
 | 
			
		||||
		RMap<String, T> rMap = redissonClient.getMap(key);
 | 
			
		||||
		return rMap.getAll(rMap.keySet());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 往Hash中存入数据
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key   Redis键
 | 
			
		||||
	 * @param hKey  Hash键
 | 
			
		||||
	 * @param value 值
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
 | 
			
		||||
		RMap<String, T> rMap = redissonClient.getMap(key);
 | 
			
		||||
		rMap.put(hKey, value);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获取Hash中的数据
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key  Redis键
 | 
			
		||||
	 * @param hKey Hash键
 | 
			
		||||
	 * @return Hash中的对象
 | 
			
		||||
	 */
 | 
			
		||||
	public <T> T getCacheMapValue(final String key, final String hKey) {
 | 
			
		||||
		RMap<String, T> rMap = redissonClient.getMap(key);
 | 
			
		||||
		return rMap.get(hKey);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获取多个Hash中的数据
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param key   Redis键
 | 
			
		||||
	 * @param hKeys Hash键集合
 | 
			
		||||
	 * @return Hash对象集合
 | 
			
		||||
	 */
 | 
			
		||||
	public <K,V> Map<K,V> getMultiCacheMapValue(final String key, final Set<K> hKeys) {
 | 
			
		||||
		RMap<K,V>  rMap = redissonClient.getMap(key);
 | 
			
		||||
		return rMap.getAll(hKeys);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 获得缓存的基本对象列表
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param pattern 字符串前缀
 | 
			
		||||
	 * @return 对象列表
 | 
			
		||||
	 */
 | 
			
		||||
	public Collection<String> keys(final String pattern) {
 | 
			
		||||
		Iterable<String> iterable = redissonClient.getKeys().getKeysByPattern(pattern);
 | 
			
		||||
		return Lists.newArrayList(iterable);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -27,6 +27,9 @@ public class AddressUtils {
 | 
			
		||||
 | 
			
		||||
	public static String getRealAddressByIP(String ip) {
 | 
			
		||||
		String address = UNKNOWN;
 | 
			
		||||
		if (StringUtils.isBlank(ip)){
 | 
			
		||||
			return address;
 | 
			
		||||
		}
 | 
			
		||||
		// 内网不查询
 | 
			
		||||
		ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
 | 
			
		||||
		if (NetUtil.isInnerIP(ip)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
package com.ruoyi.framework.config;
 | 
			
		||||
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import com.ruoyi.common.utils.ServletUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 服务相关配置
 | 
			
		||||
 * 
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
@Component
 | 
			
		||||
public class ServerConfig
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取完整的请求路径,包括:域名,端口,上下文访问路径
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 服务地址
 | 
			
		||||
     */
 | 
			
		||||
    public String getUrl()
 | 
			
		||||
    {
 | 
			
		||||
        HttpServletRequest request = ServletUtils.getRequest();
 | 
			
		||||
        return getDomain(request);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static String getDomain(HttpServletRequest request)
 | 
			
		||||
    {
 | 
			
		||||
        StringBuffer url = request.getRequestURL();
 | 
			
		||||
        String contextPath = request.getServletContext().getContextPath();
 | 
			
		||||
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
package com.ruoyi.framework.config;
 | 
			
		||||
 | 
			
		||||
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
 | 
			
		||||
import com.ruoyi.common.properties.TokenProperties;
 | 
			
		||||
import com.ruoyi.common.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.utils.spring.SpringUtils;
 | 
			
		||||
import com.ruoyi.framework.config.properties.SwaggerProperties;
 | 
			
		||||
import io.swagger.annotations.ApiOperation;
 | 
			
		||||
import io.swagger.models.auth.In;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import springfox.documentation.builders.ApiInfoBuilder;
 | 
			
		||||
import springfox.documentation.builders.PathSelectors;
 | 
			
		||||
@@ -15,6 +16,7 @@ import springfox.documentation.spi.DocumentationType;
 | 
			
		||||
import springfox.documentation.spi.service.contexts.SecurityContext;
 | 
			
		||||
import springfox.documentation.spring.web.plugins.Docket;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.PostConstruct;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@@ -27,82 +29,92 @@ import java.util.List;
 | 
			
		||||
@EnableKnife4j
 | 
			
		||||
public class SwaggerConfig {
 | 
			
		||||
 | 
			
		||||
	@Autowired
 | 
			
		||||
	private SwaggerProperties swaggerProperties;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private SwaggerProperties swaggerProperties;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 创建API
 | 
			
		||||
	 */
 | 
			
		||||
	@Bean
 | 
			
		||||
	public Docket createRestApi() {
 | 
			
		||||
		return new Docket(DocumentationType.OAS_30)
 | 
			
		||||
			.enable(swaggerProperties.getEnabled())
 | 
			
		||||
			// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
 | 
			
		||||
			.apiInfo(apiInfo())
 | 
			
		||||
			// 设置哪些接口暴露给Swagger展示
 | 
			
		||||
			.select()
 | 
			
		||||
			// 扫描所有有注解的api,用这种方式更灵活
 | 
			
		||||
			.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
 | 
			
		||||
			// 扫描指定包中的swagger注解
 | 
			
		||||
			// .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
 | 
			
		||||
			// 扫描所有 .apis(RequestHandlerSelectors.any())
 | 
			
		||||
			.paths(PathSelectors.any())
 | 
			
		||||
			.build()
 | 
			
		||||
			/* 设置安全模式,swagger可以设置访问token */
 | 
			
		||||
			.securitySchemes(securitySchemes())
 | 
			
		||||
			.securityContexts(securityContexts())
 | 
			
		||||
			.pathMapping(swaggerProperties.getPathMapping());
 | 
			
		||||
	}
 | 
			
		||||
    @Autowired
 | 
			
		||||
	private TokenProperties tokenProperties;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 安全模式,这里指定token通过Authorization头请求头传递
 | 
			
		||||
	 */
 | 
			
		||||
	private List<SecurityScheme> securitySchemes() {
 | 
			
		||||
		List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
 | 
			
		||||
		apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
 | 
			
		||||
		return apiKeyList;
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建API
 | 
			
		||||
     */
 | 
			
		||||
    @PostConstruct
 | 
			
		||||
    public void createRestApi() {
 | 
			
		||||
		for (SwaggerProperties.Groups group : swaggerProperties.getGroups()) {
 | 
			
		||||
			String basePackage = group.getBasePackage();
 | 
			
		||||
			Docket docket = new Docket(DocumentationType.OAS_30)
 | 
			
		||||
					.enable(swaggerProperties.getEnabled())
 | 
			
		||||
					// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
 | 
			
		||||
					.apiInfo(apiInfo())
 | 
			
		||||
					// 设置哪些接口暴露给Swagger展示
 | 
			
		||||
					.select()
 | 
			
		||||
					// 扫描所有有注解的api,用这种方式更灵活
 | 
			
		||||
					//.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
 | 
			
		||||
					// 扫描指定包中的swagger注解
 | 
			
		||||
					.apis(RequestHandlerSelectors.basePackage(basePackage))
 | 
			
		||||
					// 扫描所有 .apis(RequestHandlerSelectors.any())
 | 
			
		||||
					.paths(PathSelectors.any())
 | 
			
		||||
					.build()
 | 
			
		||||
					.groupName(group.getName())
 | 
			
		||||
					// 设置安全模式,swagger可以设置访问token
 | 
			
		||||
					.securitySchemes(securitySchemes())
 | 
			
		||||
					.securityContexts(securityContexts())
 | 
			
		||||
					.pathMapping(swaggerProperties.getPathMapping());
 | 
			
		||||
			String beanName = StringUtils.substringAfterLast(basePackage, ".") + "Docket";
 | 
			
		||||
			SpringUtils.registerBean(beanName, docket);
 | 
			
		||||
		}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 安全上下文
 | 
			
		||||
	 */
 | 
			
		||||
	private List<SecurityContext> securityContexts() {
 | 
			
		||||
		List<SecurityContext> securityContexts = new ArrayList<>();
 | 
			
		||||
		securityContexts.add(
 | 
			
		||||
			SecurityContext.builder()
 | 
			
		||||
				.securityReferences(defaultAuth())
 | 
			
		||||
				.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
 | 
			
		||||
				.build());
 | 
			
		||||
		return securityContexts;
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * 安全模式,这里指定token通过Authorization头请求头传递
 | 
			
		||||
     */
 | 
			
		||||
    private List<SecurityScheme> securitySchemes() {
 | 
			
		||||
        List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
 | 
			
		||||
		String header = tokenProperties.getHeader();
 | 
			
		||||
		apiKeyList.add(new ApiKey(header, header, In.HEADER.toValue()));
 | 
			
		||||
        return apiKeyList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 默认的安全上引用
 | 
			
		||||
	 */
 | 
			
		||||
	private List<SecurityReference> defaultAuth() {
 | 
			
		||||
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
 | 
			
		||||
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
 | 
			
		||||
		authorizationScopes[0] = authorizationScope;
 | 
			
		||||
		List<SecurityReference> securityReferences = new ArrayList<>();
 | 
			
		||||
		securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
 | 
			
		||||
		return securityReferences;
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * 安全上下文
 | 
			
		||||
     */
 | 
			
		||||
    private List<SecurityContext> securityContexts() {
 | 
			
		||||
        List<SecurityContext> securityContexts = new ArrayList<>();
 | 
			
		||||
        securityContexts.add(
 | 
			
		||||
                SecurityContext.builder()
 | 
			
		||||
                        .securityReferences(defaultAuth())
 | 
			
		||||
                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
 | 
			
		||||
                        .build());
 | 
			
		||||
        return securityContexts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 添加摘要信息
 | 
			
		||||
	 */
 | 
			
		||||
	private ApiInfo apiInfo() {
 | 
			
		||||
		// 用ApiInfoBuilder进行定制
 | 
			
		||||
		SwaggerProperties.Contact contact = swaggerProperties.getContact();
 | 
			
		||||
		return new ApiInfoBuilder()
 | 
			
		||||
			// 设置标题
 | 
			
		||||
			.title(swaggerProperties.getTitle())
 | 
			
		||||
			// 描述
 | 
			
		||||
			.description(swaggerProperties.getDescription())
 | 
			
		||||
			// 作者信息
 | 
			
		||||
			.contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail()))
 | 
			
		||||
			// 版本
 | 
			
		||||
			.version(swaggerProperties.getVersion())
 | 
			
		||||
			.build();
 | 
			
		||||
	}
 | 
			
		||||
    /**
 | 
			
		||||
     * 默认的安全上引用
 | 
			
		||||
     */
 | 
			
		||||
    private List<SecurityReference> defaultAuth() {
 | 
			
		||||
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
 | 
			
		||||
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
 | 
			
		||||
        authorizationScopes[0] = authorizationScope;
 | 
			
		||||
        List<SecurityReference> securityReferences = new ArrayList<>();
 | 
			
		||||
        securityReferences.add(new SecurityReference(tokenProperties.getHeader(), authorizationScopes));
 | 
			
		||||
        return securityReferences;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 添加摘要信息
 | 
			
		||||
     */
 | 
			
		||||
    private ApiInfo apiInfo() {
 | 
			
		||||
        // 用ApiInfoBuilder进行定制
 | 
			
		||||
        SwaggerProperties.Contact contact = swaggerProperties.getContact();
 | 
			
		||||
        return new ApiInfoBuilder()
 | 
			
		||||
                // 设置标题
 | 
			
		||||
                .title(swaggerProperties.getTitle())
 | 
			
		||||
                // 描述
 | 
			
		||||
                .description(swaggerProperties.getDescription())
 | 
			
		||||
                // 作者信息
 | 
			
		||||
                .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail()))
 | 
			
		||||
                // 版本
 | 
			
		||||
                .version(swaggerProperties.getVersion())
 | 
			
		||||
                .build();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
package com.ruoyi.framework.config.properties;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Security 配置属性
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Component
 | 
			
		||||
@ConfigurationProperties(prefix = "security")
 | 
			
		||||
public class SecurityProperties {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 退出登录url
 | 
			
		||||
     */
 | 
			
		||||
    private String logoutUrl;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 匿名放行路径
 | 
			
		||||
     */
 | 
			
		||||
    private String[] anonymous;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户任意访问放行路径
 | 
			
		||||
     */
 | 
			
		||||
    private String[] permitAll;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -5,6 +5,8 @@ import lombok.NoArgsConstructor;
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * swagger 配置属性
 | 
			
		||||
 *
 | 
			
		||||
@@ -41,23 +43,46 @@ public class SwaggerProperties {
 | 
			
		||||
	 */
 | 
			
		||||
    private Contact contact;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 组配置
 | 
			
		||||
	 */
 | 
			
		||||
	private List<Groups> groups;
 | 
			
		||||
 | 
			
		||||
    @Data
 | 
			
		||||
	@NoArgsConstructor
 | 
			
		||||
	public static class Contact{
 | 
			
		||||
	public static class Contact {
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 联系人
 | 
			
		||||
		 **/
 | 
			
		||||
		 */
 | 
			
		||||
		private String name;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 联系人url
 | 
			
		||||
		 **/
 | 
			
		||||
		 */
 | 
			
		||||
		private String url;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 联系人email
 | 
			
		||||
		 **/
 | 
			
		||||
		 */
 | 
			
		||||
		private String email;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Data
 | 
			
		||||
	@NoArgsConstructor
 | 
			
		||||
	public static class Groups {
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 组名
 | 
			
		||||
		 */
 | 
			
		||||
		private String name;
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		 * 基础包路径
 | 
			
		||||
		 */
 | 
			
		||||
		private String basePackage;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -557,7 +557,7 @@ export default {
 | 
			
		||||
    /** ${subTable.functionName}删除按钮操作 */
 | 
			
		||||
    handleDelete${subClassName}() {
 | 
			
		||||
      if (this.checked${subClassName}.length == 0) {
 | 
			
		||||
        this.msgError("请先选择要删除的${subTable.functionName}数据");
 | 
			
		||||
        this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
 | 
			
		||||
      } else {
 | 
			
		||||
        const ${subclassName}List = this.${subclassName}List;
 | 
			
		||||
        const checked${subClassName} = this.checked${subClassName};
 | 
			
		||||
 
 | 
			
		||||
@@ -77,9 +77,14 @@ public class SysLoginService
 | 
			
		||||
            asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"), request);
 | 
			
		||||
            throw new UserPasswordNotMatchException();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            asyncService.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage(), request);
 | 
			
		||||
            throw new ServiceException(e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		asyncService.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"), request);
 | 
			
		||||
        recordLoginInfo(user);
 | 
			
		||||
        recordLoginInfo(user.getUserId());
 | 
			
		||||
        // 生成token
 | 
			
		||||
        StpUtil.login(user.getUserId(), "PC");
 | 
			
		||||
        return StpUtil.getTokenValue();
 | 
			
		||||
@@ -109,12 +114,15 @@ public class SysLoginService
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 记录登录信息
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户ID
 | 
			
		||||
     */
 | 
			
		||||
    public void recordLoginInfo(SysUser user)
 | 
			
		||||
    public void recordLoginInfo(Long userId)
 | 
			
		||||
    {
 | 
			
		||||
        user.setLoginIp(ServletUtils.getClientIP());
 | 
			
		||||
        user.setLoginDate(DateUtils.getNowDate());
 | 
			
		||||
		user.setUpdateBy(user.getUserName());
 | 
			
		||||
        userService.updateUserProfile(user);
 | 
			
		||||
        SysUser sysUser = new SysUser();
 | 
			
		||||
        sysUser.setUserId(userId);
 | 
			
		||||
        sysUser.setLoginIp(ServletUtils.getClientIP());
 | 
			
		||||
        sysUser.setLoginDate(DateUtils.getNowDate());
 | 
			
		||||
        userService.updateUserProfile(sysUser);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,8 +4,8 @@
 | 
			
		||||
      <el-tab-pane label="基本信息" name="basic">
 | 
			
		||||
        <basic-info-form ref="basicInfo" :info="info" />
 | 
			
		||||
      </el-tab-pane>
 | 
			
		||||
      <el-tab-pane label="字段信息" name="cloum">
 | 
			
		||||
        <el-table ref="dragTable" :data="cloumns" row-key="columnId" :max-height="tableHeight">
 | 
			
		||||
      <el-tab-pane label="字段信息" name="columnInfo">
 | 
			
		||||
        <el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
 | 
			
		||||
          <el-table-column label="序号" type="index" min-width="5%" class-name="allowDrag" />
 | 
			
		||||
          <el-table-column
 | 
			
		||||
            label="字段列名"
 | 
			
		||||
@@ -141,13 +141,13 @@ export default {
 | 
			
		||||
  data() {
 | 
			
		||||
    return {
 | 
			
		||||
      // 选中选项卡的 name
 | 
			
		||||
      activeName: "cloum",
 | 
			
		||||
      activeName: "columnInfo",
 | 
			
		||||
      // 表格的高度
 | 
			
		||||
      tableHeight: document.documentElement.scrollHeight - 245 + "px",
 | 
			
		||||
      // 表信息
 | 
			
		||||
      tables: [],
 | 
			
		||||
      // 表列信息
 | 
			
		||||
      cloumns: [],
 | 
			
		||||
      columns: [],
 | 
			
		||||
      // 字典信息
 | 
			
		||||
      dictOptions: [],
 | 
			
		||||
      // 菜单信息
 | 
			
		||||
@@ -161,7 +161,7 @@ export default {
 | 
			
		||||
    if (tableId) {
 | 
			
		||||
      // 获取表详细信息
 | 
			
		||||
      getGenTable(tableId).then(res => {
 | 
			
		||||
        this.cloumns = res.data.rows;
 | 
			
		||||
        this.columns = res.data.rows;
 | 
			
		||||
        this.info = res.data.info;
 | 
			
		||||
        this.tables = res.data.tables;
 | 
			
		||||
      });
 | 
			
		||||
@@ -184,7 +184,7 @@ export default {
 | 
			
		||||
        const validateResult = res.every(item => !!item);
 | 
			
		||||
        if (validateResult) {
 | 
			
		||||
          const genTable = Object.assign({}, basicForm.model, genForm.model);
 | 
			
		||||
          genTable.columns = this.cloumns;
 | 
			
		||||
          genTable.columns = this.columns;
 | 
			
		||||
          genTable.params = {
 | 
			
		||||
            treeCode: genTable.treeCode,
 | 
			
		||||
            treeName: genTable.treeName,
 | 
			
		||||
@@ -220,10 +220,10 @@ export default {
 | 
			
		||||
    const sortable = Sortable.create(el, {
 | 
			
		||||
      handle: ".allowDrag",
 | 
			
		||||
      onEnd: evt => {
 | 
			
		||||
        const targetRow = this.cloumns.splice(evt.oldIndex, 1)[0];
 | 
			
		||||
        this.cloumns.splice(evt.newIndex, 0, targetRow);
 | 
			
		||||
        for (let index in this.cloumns) {
 | 
			
		||||
          this.cloumns[index].sort = parseInt(index) + 1;
 | 
			
		||||
        const targetRow = this.columns.splice(evt.oldIndex, 1)[0];
 | 
			
		||||
        this.columns.splice(evt.newIndex, 0, targetRow);
 | 
			
		||||
        for (let index in this.columns) {
 | 
			
		||||
          this.columns[index].sort = parseInt(index) + 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user