mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-24 07:19:46 +08:00
版本更新 v2.2.0
This commit is contained in:
@ -12,7 +12,7 @@ import java.util.Set;
|
||||
|
||||
/**
|
||||
* 登录用户身份权限
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
|
||||
@ -81,6 +81,7 @@ public class LoginUser implements UserDetails
|
||||
return user.getPassword();
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public String getUsername()
|
||||
{
|
||||
@ -99,7 +100,7 @@ public class LoginUser implements UserDetails
|
||||
|
||||
/**
|
||||
* 指定用户是否解锁,锁定的用户无法进行身份验证
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
@ -111,7 +112,7 @@ public class LoginUser implements UserDetails
|
||||
|
||||
/**
|
||||
* 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
@ -123,7 +124,7 @@ public class LoginUser implements UserDetails
|
||||
|
||||
/**
|
||||
* 是否可用 ,禁用的用户不能身份验证
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
@ -133,6 +134,7 @@ public class LoginUser implements UserDetails
|
||||
return true;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities()
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.ruoyi.common.filter;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@ -15,7 +16,7 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* XSS过滤处理
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
|
||||
@ -94,12 +95,12 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
|
||||
|
||||
/**
|
||||
* 是否是Json请求
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public boolean isJsonRequest()
|
||||
{
|
||||
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
|
||||
return MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(header);
|
||||
return StrUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.ruoyi.framework.aspectj;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
@ -36,7 +37,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志记录处理
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Aspect
|
||||
@ -64,7 +65,7 @@ public class LogAspect
|
||||
|
||||
/**
|
||||
* 拦截异常操作
|
||||
*
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param e 异常
|
||||
*/
|
||||
@ -130,7 +131,7 @@ public class LogAspect
|
||||
|
||||
/**
|
||||
* 获取注解中对方法的描述信息 用于Controller层注解
|
||||
*
|
||||
*
|
||||
* @param log 日志
|
||||
* @param operLog 操作日志
|
||||
* @throws Exception
|
||||
@ -153,7 +154,7 @@ public class LogAspect
|
||||
|
||||
/**
|
||||
* 获取请求的参数,放到log中
|
||||
*
|
||||
*
|
||||
* @param operLog 操作日志
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
@ -198,7 +199,7 @@ public class LogAspect
|
||||
{
|
||||
for (int i = 0; i < paramsArray.length; i++)
|
||||
{
|
||||
if (!isFilterObject(paramsArray[i]))
|
||||
if (Validator.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
|
||||
{
|
||||
Object jsonObj = JSON.toJSON(paramsArray[i]);
|
||||
params += jsonObj.toString() + " ";
|
||||
@ -210,7 +211,7 @@ public class LogAspect
|
||||
|
||||
/**
|
||||
* 判断是否需要过滤的对象。
|
||||
*
|
||||
*
|
||||
* @param o 对象信息。
|
||||
* @return 如果是需要过滤的对象,则返回true;否则返回false。
|
||||
*/
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -7,15 +8,10 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
|
||||
|
||||
/**
|
||||
* redis配置
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
@ -29,16 +25,17 @@ public class RedisConfig extends CachingConfigurerSupport
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
|
||||
GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
|
||||
serializer.setObjectMapper(mapper);
|
||||
|
||||
template.setValueSerializer(serializer);
|
||||
// 使用StringRedisSerializer来序列化和反序列化redis的key值
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setValueSerializer(serializer);
|
||||
|
||||
// Hash的key也采用StringRedisSerializer的序列化方式
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setHashValueSerializer(serializer);
|
||||
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
|
@ -13,12 +13,29 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "captcha")
|
||||
public class CaptchaProperties {
|
||||
// 验证码类型
|
||||
|
||||
/**
|
||||
* 验证码开关
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 验证码类型
|
||||
*/
|
||||
private String type;
|
||||
// 验证码类别
|
||||
|
||||
/**
|
||||
* 验证码类别
|
||||
*/
|
||||
private String category;
|
||||
// 数字验证码位数
|
||||
|
||||
/**
|
||||
* 数字验证码位数
|
||||
*/
|
||||
private Integer numberLength;
|
||||
// 字符验证码长度
|
||||
|
||||
/**
|
||||
* 字符验证码长度
|
||||
*/
|
||||
private Integer charLength;
|
||||
}
|
||||
|
@ -1,26 +1,33 @@
|
||||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.CustomException;
|
||||
import com.ruoyi.common.exception.user.CaptchaException;
|
||||
import com.ruoyi.common.exception.user.CaptchaExpireException;
|
||||
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.MessageUtils;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.ip.IpUtils;
|
||||
import com.ruoyi.framework.config.properties.CaptchaProperties;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
*
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Component
|
||||
@ -35,9 +42,15 @@ public class SysLoginService
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Autowired
|
||||
private CaptchaProperties captchaProperties;
|
||||
|
||||
@Autowired
|
||||
private ISysUserService userService;
|
||||
|
||||
/**
|
||||
* 登录验证
|
||||
*
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @param code 验证码
|
||||
@ -46,19 +59,19 @@ public class SysLoginService
|
||||
*/
|
||||
public String login(String username, String password, String code, String uuid)
|
||||
{
|
||||
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
|
||||
String captcha = redisCache.getCacheObject(verifyKey);
|
||||
redisCache.deleteObject(verifyKey);
|
||||
if (captcha == null)
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
|
||||
throw new CaptchaExpireException();
|
||||
}
|
||||
if (!code.equalsIgnoreCase(captcha))
|
||||
{
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
|
||||
throw new CaptchaException();
|
||||
}
|
||||
if(captchaProperties.getEnabled()) {
|
||||
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
|
||||
String captcha = redisCache.getCacheObject(verifyKey);
|
||||
redisCache.deleteObject(verifyKey);
|
||||
if (captcha == null) {
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
|
||||
throw new CaptchaExpireException();
|
||||
}
|
||||
if (!code.equalsIgnoreCase(captcha)) {
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
|
||||
throw new CaptchaException();
|
||||
}
|
||||
}
|
||||
// 用户验证
|
||||
Authentication authentication = null;
|
||||
try
|
||||
@ -82,7 +95,19 @@ public class SysLoginService
|
||||
}
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
recordLoginInfo(loginUser.getUser());
|
||||
// 生成token
|
||||
return tokenService.createToken(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*/
|
||||
public void recordLoginInfo(SysUser user)
|
||||
{
|
||||
user.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
|
||||
user.setLoginDate(DateUtils.getNowDate());
|
||||
user.setUpdateBy(user.getUserName());
|
||||
userService.updateUserProfile(user);
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,7 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
|
||||
.set(cenTableColumn.getIsEdit() == null, GenTableColumn::getIsEdit, null)
|
||||
.set(cenTableColumn.getIsList() == null, GenTableColumn::getIsList, null)
|
||||
.set(cenTableColumn.getIsQuery() == null, GenTableColumn::getIsQuery, null)
|
||||
.set(cenTableColumn.getIsRequired() == null, GenTableColumn::getIsRequired, null)
|
||||
.eq(GenTableColumn::getColumnId,cenTableColumn.getColumnId()));
|
||||
}
|
||||
}
|
||||
@ -458,4 +459,4 @@ public class GenTableServiceImpl extends ServiceImpl<GenTableMapper, GenTable> i
|
||||
}
|
||||
return genPath + File.separator + VelocityUtils.getFileName(template, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,12 @@ public class CaptchaController {
|
||||
*/
|
||||
@GetMapping("/captchaImage")
|
||||
public AjaxResult getCode() {
|
||||
Map<String, Object> ajax = new HashMap<>();
|
||||
Boolean enabled = captchaProperties.getEnabled();
|
||||
ajax.put("enabled", enabled);
|
||||
if (!enabled) {
|
||||
return AjaxResult.success(ajax);
|
||||
}
|
||||
// 保存验证码信息
|
||||
String uuid = IdUtil.simpleUUID();
|
||||
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
|
||||
@ -90,7 +96,6 @@ public class CaptchaController {
|
||||
code = captcha.getCode();
|
||||
}
|
||||
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
|
||||
Map<String,Object> ajax = new HashMap<>();
|
||||
ajax.put("uuid", uuid);
|
||||
ajax.put("img", captcha.getImageBase64());
|
||||
return AjaxResult.success(ajax);
|
||||
|
@ -14,6 +14,8 @@ ruoyi:
|
||||
addressEnabled: false
|
||||
|
||||
captcha:
|
||||
# 验证码开关
|
||||
enabled: true
|
||||
# 验证码类型 math 数组计算 char 字符验证
|
||||
type: math
|
||||
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
|
||||
|
@ -29,10 +29,12 @@ public class ${ClassName}AddBo {
|
||||
#if($column.javaType == 'Date')
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
#end
|
||||
#if($column.isRequired==1)
|
||||
#if($column.javaType == 'String')
|
||||
@NotBlank(message = "$column.columnComment不能为空")
|
||||
#else
|
||||
@NotNull(message = "$column.columnComment不能为空")
|
||||
#end
|
||||
#end
|
||||
private $column.javaType $column.javaField;
|
||||
#end
|
||||
|
@ -29,10 +29,12 @@ public class ${ClassName}EditBo {
|
||||
#if($column.javaType == 'Date')
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
#end
|
||||
#if($column.isRequired==1)
|
||||
#if($column.javaType == 'String')
|
||||
@NotBlank(message = "$column.columnComment不能为空")
|
||||
#else
|
||||
@NotNull(message = "$column.columnComment不能为空")
|
||||
#end
|
||||
#end
|
||||
private $column.javaType $column.javaField;
|
||||
#end
|
||||
|
@ -108,6 +108,7 @@
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
size="mini"
|
||||
:loading="exportLoading"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['${moduleName}:${businessName}:export']"
|
||||
>导出</el-button>
|
||||
@ -158,7 +159,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
|
||||
<pagination
|
||||
v-show="total>0"
|
||||
:total="total"
|
||||
@ -354,6 +355,8 @@ export default {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: true,
|
||||
// 导出遮罩层
|
||||
exportLoading: false,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
#if($table.sub)
|
||||
@ -630,10 +633,12 @@ export default {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(function() {
|
||||
}).then(() => {
|
||||
this.exportLoading = true;
|
||||
return export${BusinessName}(queryParams);
|
||||
}).then(response => {
|
||||
this.download(response.msg);
|
||||
this.exportLoading = false;
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user