新增: 1. 新增EventBus 2. 新增 邮箱、手机、用户名 + 密码登陆 3. 新增 邮箱 + 验证码 登录 4. 新增 手机号 + 验证码 登录 5. 新增个人修改手机号 前置身份校验 6. 新增个人修改邮箱 前置身份校验 7. 新增个人中心忘记密码 前置身份校验 8. 新增使用ResultWrapper 代替 ResultVo 返回数据 优化: 1. 优化接口加密 2. 优化CrudService实现 3. 变更自动日志记录模式到自定义记录 4. 优化导出Excel模式为认证模式 5. 优化用户Redis缓存 6. 移除Shiro,变更使用SpringSecurity 作为权限验证 升级/修复: 1. 修复 SecurityCache 缓存穿透BUG 2. 升级 mybatis-plus 版本至 3.5.2 修复若干CVE 3. 升级 springboot 版本至 2.5.6 修复若干CVE 4. 升级 mysql版本至8.0.28 修复 CVE-2022-21363 5. 升级 guava 版本至 30.0.android 修复 修复 CVE-2020-8908 6. 升级 bouncycastle 版本至 1.69 修复 Cxa9261daf-3755 7. 升级 protobuf-java 版本至 3.18.2 修复 CVE-2021-22569 8. 升级 logback 版本至 修复 CVE-2021-42550pull/19/head
parent
077196058c
commit
edd6dcec59
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,14 @@
|
||||
FROM mysql:8.0.19
|
||||
|
||||
MAINTAINER opsli.com
|
||||
LABEL version=V1.3.3
|
||||
LABEL description=OPSLI-快速开发平台
|
||||
LABEL qqGroup=724850675
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
|
||||
# 切换为上海时区
|
||||
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime \
|
||||
&& echo $TZ > /etc/timezone
|
||||
|
||||
COPY ./opsli-boot.sql /docker-entrypoint-initdb.d
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.api.base.encrypt;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.opsli.common.annotation.validator.Validator;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMax;
|
||||
import org.opsli.common.enums.ValidatorType;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 登陆 加解密
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2021-01-24 12:48 下午
|
||||
**/
|
||||
@Data
|
||||
public class EncryptModel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
@ValidatorLenMax(2000)
|
||||
@ApiModelProperty(value = "加密数据")
|
||||
private String encryptData;
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.api.wrapper.system.logs;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.opsli.api.base.warpper.ApiWrapper;
|
||||
import org.opsli.common.annotation.validator.Validator;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMax;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMin;
|
||||
import org.opsli.common.enums.ValidatorType;
|
||||
import org.opsli.plugins.excel.annotation.ExcelInfo;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/**
|
||||
* 行为日志 Model
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-26 19:21:57
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class OperationLogModel extends ApiWrapper {
|
||||
|
||||
/** 多租户字段 */
|
||||
private String tenantId;
|
||||
|
||||
/** 日志等级 */
|
||||
@ApiModelProperty(value = "日志等级")
|
||||
@ExcelProperty(value = "日志等级", order = 1)
|
||||
@ExcelInfo( dictType = "log_level" )
|
||||
@ValidatorLenMax(8)
|
||||
private String level;
|
||||
|
||||
/** 被操作的系统模块 */
|
||||
@ApiModelProperty(value = "被操作的系统模块")
|
||||
@ExcelProperty(value = "被操作的系统模块", order = 2)
|
||||
@ExcelInfo( dictType = "log_model_type" )
|
||||
@ValidatorLenMax(20)
|
||||
private String moduleId;
|
||||
|
||||
/** 方法名 */
|
||||
@ApiModelProperty(value = "方法名")
|
||||
@ExcelProperty(value = "方法名", order = 3)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(100)
|
||||
private String method;
|
||||
|
||||
/** 参数 */
|
||||
@ApiModelProperty(value = "参数")
|
||||
@ExcelProperty(value = "参数", order = 4)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(20000)
|
||||
private String args;
|
||||
|
||||
/** 操作人id */
|
||||
@ApiModelProperty(value = "操作人id")
|
||||
@ExcelProperty(value = "操作人id", order = 5)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(19)
|
||||
private String userId;
|
||||
|
||||
/** 操作账号 */
|
||||
@ApiModelProperty(value = "操作账号")
|
||||
@ExcelProperty(value = "操作账号", order = 6)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(32)
|
||||
private String username;
|
||||
|
||||
/** 操作人真实名称 */
|
||||
@ApiModelProperty(value = "操作人真实名称")
|
||||
@ExcelProperty(value = "操作人真实名称", order = 7)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(50)
|
||||
private String realName;
|
||||
|
||||
/** 日志描述 */
|
||||
@ApiModelProperty(value = "日志描述")
|
||||
@ExcelProperty(value = "日志描述", order = 8)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(255)
|
||||
private String description;
|
||||
|
||||
/** 操作类型 */
|
||||
@ApiModelProperty(value = "操作类型")
|
||||
@ExcelProperty(value = "操作类型", order = 9)
|
||||
@ExcelInfo( dictType = "log_operation_type" )
|
||||
@ValidatorLenMax(20)
|
||||
private String operationType;
|
||||
|
||||
/** 方法运行时间 */
|
||||
@ApiModelProperty(value = "方法运行时间")
|
||||
@ExcelProperty(value = "方法运行时间", order = 10)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(19)
|
||||
private String runTime;
|
||||
|
||||
/** 方法返回值 */
|
||||
@ApiModelProperty(value = "方法返回值")
|
||||
@ExcelProperty(value = "方法返回值", order = 11)
|
||||
@ExcelInfo
|
||||
@ValidatorLenMax(20000)
|
||||
private String returnValue;
|
||||
|
||||
/** 日志请求类型 */
|
||||
@ApiModelProperty(value = "日志请求类型")
|
||||
@ExcelProperty(value = "日志请求类型", order = 12)
|
||||
@ExcelInfo( dictType = "log_type" )
|
||||
@ValidatorLenMax(8)
|
||||
private String logType;
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.api.wrapper.system.user;
|
||||
|
||||
import lombok.Data;
|
||||
import org.opsli.common.annotation.validator.Validator;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMax;
|
||||
import org.opsli.common.enums.ValidatorType;
|
||||
|
||||
/**
|
||||
* 启用用户
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-16 8:14 PM
|
||||
**/
|
||||
@Data
|
||||
public class EnableUserModel {
|
||||
|
||||
/** 主键 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
@ValidatorLenMax(50)
|
||||
private String userId;
|
||||
|
||||
/** 是否启用 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
@ValidatorLenMax(10)
|
||||
private String enabled;
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.api.wrapper.system.user;
|
||||
|
||||
import lombok.Data;
|
||||
import org.opsli.common.annotation.validator.Validator;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMax;
|
||||
import org.opsli.common.enums.ValidatorType;
|
||||
|
||||
/**
|
||||
* 修改用户邮箱
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-16 8:14 PM
|
||||
**/
|
||||
@Data
|
||||
public class UpdateUserEmailModel {
|
||||
|
||||
/** 主键 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL, ValidatorType.IS_EMAIL})
|
||||
@ValidatorLenMax(50)
|
||||
private String email;
|
||||
|
||||
/** 验证码 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
@ValidatorLenMax(20)
|
||||
private String verificationCode;
|
||||
|
||||
/** 凭证 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
private String certificate;
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.api.wrapper.system.user;
|
||||
|
||||
import lombok.Data;
|
||||
import org.opsli.common.annotation.validator.Validator;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMax;
|
||||
import org.opsli.common.enums.ValidatorType;
|
||||
|
||||
/**
|
||||
* 修改用户手机
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-16 8:14 PM
|
||||
**/
|
||||
@Data
|
||||
public class UpdateUserMobileModel {
|
||||
|
||||
/** 主键 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL, ValidatorType.IS_MOBILE})
|
||||
@ValidatorLenMax(50)
|
||||
private String mobile;
|
||||
|
||||
/** 验证码 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
@ValidatorLenMax(20)
|
||||
private String verificationCode;
|
||||
|
||||
/** 凭证 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
private String certificate;
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.api.wrapper.system.user;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.opsli.common.annotation.validator.Validator;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMax;
|
||||
import org.opsli.common.annotation.validator.ValidatorLenMin;
|
||||
import org.opsli.common.enums.ValidatorType;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户 修改密码
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2020-09-16 17:33
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@ExcelIgnoreUnannotated
|
||||
public class UpdateUserPasswordByForgetModel implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 新密码 */
|
||||
@ApiModelProperty(value = "新密码")
|
||||
@Validator({ValidatorType.IS_NOT_NULL, ValidatorType.IS_SECURITY_PASSWORD})
|
||||
@ValidatorLenMin(6)
|
||||
@ValidatorLenMax(50)
|
||||
private String newPassword;
|
||||
|
||||
/** 凭证 */
|
||||
@Validator({ValidatorType.IS_NOT_NULL})
|
||||
private String certificate;
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.common.enums;
|
||||
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 登录类型
|
||||
*
|
||||
* @author 周鹏程
|
||||
* @date 2020-09-17 23:40
|
||||
*/
|
||||
public enum LoginFromEnum {
|
||||
|
||||
/** 账号 */
|
||||
PC("0"),
|
||||
/** APP - 安卓 */
|
||||
APP_ANDROID("1"),
|
||||
/** APP - 苹果 */
|
||||
APP_IOS("2"),
|
||||
/** 微信小程序 */
|
||||
WX_APPLET("3"),
|
||||
/** h5 */
|
||||
H5("4"),
|
||||
/** 未知 */
|
||||
UNKNOWN("-1"),
|
||||
|
||||
;
|
||||
|
||||
private final static String FILED_NAME = "loginFrom";
|
||||
/***/
|
||||
private final String type;
|
||||
|
||||
LoginFromEnum(String type){
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public static LoginFromEnum getByCode(String code){
|
||||
return Arrays.stream(LoginFromEnum.values())
|
||||
.filter(value -> value.getType().equals(code))
|
||||
.findFirst().orElse(LoginFromEnum.UNKNOWN);
|
||||
}
|
||||
|
||||
public static LoginFromEnum getByBean(Object bean){
|
||||
if(null == bean || !BeanUtil.isBean(bean.getClass())){
|
||||
return LoginFromEnum.UNKNOWN;
|
||||
}
|
||||
|
||||
Object fieldValue = BeanUtil.getFieldValue(bean, FILED_NAME);
|
||||
if(null == fieldValue){
|
||||
return LoginFromEnum.UNKNOWN;
|
||||
}
|
||||
|
||||
return getByCode((String) fieldValue);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.common.enums;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 登陆类型
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-21 2:07 PM
|
||||
**/
|
||||
public enum LoginModelType {
|
||||
|
||||
/** 未知 */
|
||||
UNKNOWN,
|
||||
|
||||
/** 账号 */
|
||||
ACCOUNT,
|
||||
|
||||
/** 手机 */
|
||||
MOBILE,
|
||||
|
||||
/** 邮箱 */
|
||||
EMAIL
|
||||
|
||||
;
|
||||
|
||||
public static LoginModelType getTypeByStr(String principal){
|
||||
if(StrUtil.isBlank(principal)){
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
// 手机号
|
||||
if(Validator.isMobile(principal)){
|
||||
return MOBILE;
|
||||
}
|
||||
// 邮箱
|
||||
else if(Validator.isEmail(principal)){
|
||||
return EMAIL;
|
||||
}
|
||||
|
||||
// 默认账号
|
||||
return ACCOUNT;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 验证类型
|
||||
*
|
||||
* @author 周鹏程
|
||||
* @date 2022-08-01 12:49 PM
|
||||
**/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum VerificationTypeEnum {
|
||||
|
||||
/** 类型 */
|
||||
LOGIN("0"),
|
||||
|
||||
AUTH("1")
|
||||
|
||||
;
|
||||
|
||||
/** 类型 */
|
||||
private final String type;
|
||||
|
||||
public static Optional<VerificationTypeEnum> getEnumByType(String type){
|
||||
VerificationTypeEnum[] types = values();
|
||||
for (VerificationTypeEnum typeEnum : types) {
|
||||
if(typeEnum.type.equals(type)){
|
||||
return Optional.of(typeEnum);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,388 @@
|
||||
package org.opsli.common.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Hashids designed for Generating short hashes from numbers (like YouTube and Bitly), obfuscate
|
||||
* database IDs, use them as forgotten password hashes, invitation codes, store shard numbers.
|
||||
* <p>
|
||||
* This is implementation of http://hashids.org v1.0.0 version.
|
||||
*
|
||||
* This implementation is immutable, thread-safe, no lock is necessary.
|
||||
*
|
||||
* @author <a href="mailto:fanweixiao@gmail.com">fanweixiao</a>
|
||||
* @author <a href="mailto:terciofilho@gmail.com">Tercio Gaudencio Filho</a>
|
||||
* @since 0.3.3
|
||||
*/
|
||||
public class HashIdsUtil {
|
||||
/**
|
||||
* Max number that can be encoded with Hashids.
|
||||
*/
|
||||
public static final long MAX_NUMBER = 9007199254740992L;
|
||||
|
||||
private static final String DEFAULT_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
private static final String DEFAULT_SEPS = "cfhistuCFHISTU";
|
||||
private static final String DEFAULT_SALT = "";
|
||||
|
||||
private static final int DEFAULT_MIN_HASH_LENGTH = 0;
|
||||
private static final int MIN_ALPHABET_LENGTH = 16;
|
||||
private static final double SEP_DIV = 3.5;
|
||||
private static final int GUARD_DIV = 12;
|
||||
|
||||
private final String salt;
|
||||
private final int minHashLength;
|
||||
private final String alphabet;
|
||||
private final String seps;
|
||||
private final String guards;
|
||||
|
||||
public HashIdsUtil() {
|
||||
this(DEFAULT_SALT);
|
||||
}
|
||||
|
||||
public HashIdsUtil(String salt) {
|
||||
this(salt, 0);
|
||||
}
|
||||
|
||||
public HashIdsUtil(String salt, int minHashLength) {
|
||||
this(salt, minHashLength, DEFAULT_ALPHABET);
|
||||
}
|
||||
|
||||
public HashIdsUtil(String salt, int minHashLength, String alphabet) {
|
||||
this.salt = salt != null ? salt : DEFAULT_SALT;
|
||||
this.minHashLength = minHashLength > 0 ? minHashLength : DEFAULT_MIN_HASH_LENGTH;
|
||||
|
||||
final StringBuilder uniqueAlphabet = new StringBuilder();
|
||||
for (int i = 0; i < alphabet.length(); i++) {
|
||||
if (uniqueAlphabet.indexOf(String.valueOf(alphabet.charAt(i))) == -1) {
|
||||
uniqueAlphabet.append(alphabet.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
alphabet = uniqueAlphabet.toString();
|
||||
|
||||
if (alphabet.length() < MIN_ALPHABET_LENGTH) {
|
||||
throw new IllegalArgumentException(
|
||||
"alphabet must contain at least " + MIN_ALPHABET_LENGTH + " unique characters");
|
||||
}
|
||||
|
||||
if (alphabet.contains(" ")) {
|
||||
throw new IllegalArgumentException("alphabet cannot contains spaces");
|
||||
}
|
||||
|
||||
// seps should contain only characters present in alphabet;
|
||||
// alphabet should not contains seps
|
||||
String seps = DEFAULT_SEPS;
|
||||
for (int i = 0; i < seps.length(); i++) {
|
||||
final int j = alphabet.indexOf(seps.charAt(i));
|
||||
if (j == -1) {
|
||||
seps = seps.substring(0, i) + " " + seps.substring(i + 1);
|
||||
} else {
|
||||
alphabet = alphabet.substring(0, j) + " " + alphabet.substring(j + 1);
|
||||
}
|
||||
}
|
||||
|
||||
alphabet = alphabet.replaceAll("\\s+", "");
|
||||
seps = seps.replaceAll("\\s+", "");
|
||||
seps = HashIdsUtil.consistentShuffle(seps, this.salt);
|
||||
|
||||
if ((seps.isEmpty()) || (((float) alphabet.length() / seps.length()) > SEP_DIV)) {
|
||||
int seps_len = (int) Math.ceil(alphabet.length() / SEP_DIV);
|
||||
|
||||
if (seps_len == 1) {
|
||||
seps_len++;
|
||||
}
|
||||
|
||||
if (seps_len > seps.length()) {
|
||||
final int diff = seps_len - seps.length();
|
||||
seps += alphabet.substring(0, diff);
|
||||
alphabet = alphabet.substring(diff);
|
||||
} else {
|
||||
seps = seps.substring(0, seps_len);
|
||||
}
|
||||
}
|
||||
|
||||
alphabet = HashIdsUtil.consistentShuffle(alphabet, this.salt);
|
||||
// use double to round up
|
||||
final int guardCount = (int) Math.ceil((double) alphabet.length() / GUARD_DIV);
|
||||
|
||||
String guards;
|
||||
if (alphabet.length() < 3) {
|
||||
guards = seps.substring(0, guardCount);
|
||||
seps = seps.substring(guardCount);
|
||||
} else {
|
||||
guards = alphabet.substring(0, guardCount);
|
||||
alphabet = alphabet.substring(guardCount);
|
||||
}
|
||||
this.guards = guards;
|
||||
this.alphabet = alphabet;
|
||||
this.seps = seps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt numbers to string
|
||||
*
|
||||
* @param numbers
|
||||
* the numbers to encrypt
|
||||
* @return the encrypt string
|
||||
*/
|
||||
public String encode(long... numbers) {
|
||||
if (numbers.length == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
for (final long number : numbers) {
|
||||
if (number < 0) {
|
||||
return "";
|
||||
}
|
||||
if (number > MAX_NUMBER) {
|
||||
throw new IllegalArgumentException("number can not be greater than " + MAX_NUMBER + "L");
|
||||
}
|
||||
}
|
||||
return this._encode(numbers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt string to numbers
|
||||
*
|
||||
* @param hash
|
||||
* the encrypt string
|
||||
* @return decryped numbers
|
||||
*/
|
||||
public long[] decode(String hash) {
|
||||
if (hash.isEmpty()) {
|
||||
return new long[0];
|
||||
}
|
||||
|
||||
String validChars = this.alphabet + this.guards + this.seps;
|
||||
for (int i = 0; i < hash.length(); i++) {
|
||||
if(validChars.indexOf(hash.charAt(i)) == -1) {
|
||||
return new long[0];
|
||||
}
|
||||
}
|
||||
|
||||
return this._decode(hash, this.alphabet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt hexa to string
|
||||
*
|
||||
* @param hexa
|
||||
* the hexa to encrypt
|
||||
* @return the encrypt string
|
||||
*/
|
||||
public String encodeHex(String hexa) {
|
||||
if (!hexa.matches("^[0-9a-fA-F]+$")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final List<Long> matched = new ArrayList<Long>();
|
||||
final Matcher matcher = Pattern.compile("[\\w\\W]{1,12}").matcher(hexa);
|
||||
|
||||
while (matcher.find()) {
|
||||
matched.add(Long.parseLong("1" + matcher.group(), 16));
|
||||
}
|
||||
|
||||
// conversion
|
||||
final long[] result = new long[matched.size()];
|
||||
for (int i = 0; i < matched.size(); i++) {
|
||||
result[i] = matched.get(i);
|
||||
}
|
||||
|
||||
return this.encode(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt string to numbers
|
||||
*
|
||||
* @param hash
|
||||
* the encrypt string
|
||||
* @return decryped numbers
|
||||
*/
|
||||
public String decodeHex(String hash) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
final long[] numbers = this.decode(hash);
|
||||
|
||||
for (final long number : numbers) {
|
||||
result.append(Long.toHexString(number).substring(1));
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static int checkedCast(long value) {
|
||||
final int result = (int) value;
|
||||
if (result != value) {
|
||||
// don't use checkArgument here, to avoid boxing
|
||||
throw new IllegalArgumentException("Out of range: " + value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
||||
private String _encode(long... numbers) {
|
||||
long numberHashInt = 0;
|
||||
for (int i = 0; i < numbers.length; i++) {
|
||||
numberHashInt += (numbers[i] % (i + 100));
|
||||
}
|
||||
String alphabet = this.alphabet;
|
||||
final char ret = alphabet.charAt((int) (numberHashInt % alphabet.length()));
|
||||
|
||||
long num;
|
||||
long sepsIndex, guardIndex;
|
||||
String buffer;
|
||||
final StringBuilder ret_strB = new StringBuilder(this.minHashLength);
|
||||
ret_strB.append(ret);
|
||||
char guard;
|
||||
|
||||
for (int i = 0; i < numbers.length; i++) {
|
||||
num = numbers[i];
|
||||
buffer = ret + this.salt + alphabet;
|
||||
|
||||
alphabet = HashIdsUtil.consistentShuffle(alphabet, buffer.substring(0, alphabet.length()));
|
||||
final String last = HashIdsUtil.hash(num, alphabet);
|
||||
|
||||
ret_strB.append(last);
|
||||
|
||||
if (i + 1 < numbers.length) {
|
||||
if (last.length() > 0) {
|
||||
num %= (last.charAt(0) + i);
|
||||
sepsIndex = (int) (num % this.seps.length());
|
||||
} else {
|
||||
sepsIndex = 0;
|
||||
}
|
||||
ret_strB.append(this.seps.charAt((int) sepsIndex));
|
||||
}
|
||||
}
|
||||
|
||||
String ret_str = ret_strB.toString();
|
||||
if (ret_str.length() < this.minHashLength) {
|
||||
guardIndex = (numberHashInt + (ret_str.charAt(0))) % this.guards.length();
|
||||
guard = this.guards.charAt((int) guardIndex);
|
||||
|
||||
ret_str = guard + ret_str;
|
||||
|
||||
if (ret_str.length() < this.minHashLength) {
|
||||
guardIndex = (numberHashInt + (ret_str.charAt(2))) % this.guards.length();
|
||||
guard = this.guards.charAt((int) guardIndex);
|
||||
|
||||
ret_str += guard;
|
||||
}
|
||||
}
|
||||
|
||||
final int halfLen = alphabet.length() / 2;
|
||||
while (ret_str.length() < this.minHashLength) {
|
||||
alphabet = HashIdsUtil.consistentShuffle(alphabet, alphabet);
|
||||
ret_str = alphabet.substring(halfLen) + ret_str + alphabet.substring(0, halfLen);
|
||||
final int excess = ret_str.length() - this.minHashLength;
|
||||
if (excess > 0) {
|
||||
final int start_pos = excess / 2;
|
||||
ret_str = ret_str.substring(start_pos, start_pos + this.minHashLength);
|
||||
}
|
||||
}
|
||||
|
||||
return ret_str;
|
||||
}
|
||||
|
||||
private long[] _decode(String hash, String alphabet) {
|
||||
final ArrayList<Long> ret = new ArrayList<Long>();
|
||||
|
||||
int i = 0;
|
||||
final String regexp = "[" + this.guards + "]";
|
||||
String hashBreakdown = hash.replaceAll(regexp, " ");
|
||||
String[] hashArray = hashBreakdown.split(" ");
|
||||
|
||||
if (hashArray.length == 3 || hashArray.length == 2) {
|
||||
i = 1;
|
||||
}
|
||||
|
||||
if (hashArray.length > 0) {
|
||||
hashBreakdown = hashArray[i];
|
||||
if (!hashBreakdown.isEmpty()) {
|
||||
final char lottery = hashBreakdown.charAt(0);
|
||||
|
||||
hashBreakdown = hashBreakdown.substring(1);
|
||||
hashBreakdown = hashBreakdown.replaceAll("[" + this.seps + "]", " ");
|
||||
hashArray = hashBreakdown.split(" ");
|
||||
|
||||
String subHash, buffer;
|
||||
for (final String aHashArray : hashArray) {
|
||||
subHash = aHashArray;
|
||||
buffer = lottery + this.salt + alphabet;
|
||||
alphabet = HashIdsUtil.consistentShuffle(alphabet, buffer.substring(0, alphabet.length()));
|
||||
ret.add(HashIdsUtil.unhash(subHash, alphabet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// transform from List<Long> to long[]
|
||||
long[] arr = new long[ret.size()];
|
||||
for (int k = 0; k < arr.length; k++) {
|
||||
arr[k] = ret.get(k);
|
||||
}
|
||||
|
||||
if (!this.encode(arr).equals(hash)) {
|
||||
arr = new long[0];
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static String consistentShuffle(String alphabet, String salt) {
|
||||
if (salt.length() <= 0) {
|
||||
return alphabet;
|
||||
}
|
||||
|
||||
int asc_val, j;
|
||||
final char[] tmpArr = alphabet.toCharArray();
|
||||
for (int i = tmpArr.length - 1, v = 0, p = 0; i > 0; i--, v++) {
|
||||
v %= salt.length();
|
||||
asc_val = salt.charAt(v);
|
||||
p += asc_val;
|
||||
j = (asc_val + v + p) % i;
|
||||
final char tmp = tmpArr[j];
|
||||
tmpArr[j] = tmpArr[i];
|
||||
tmpArr[i] = tmp;
|
||||
}
|
||||
|
||||
return new String(tmpArr);
|
||||
}
|
||||
|
||||
private static String hash(long input, String alphabet) {
|
||||
String hash = "";
|
||||
final int alphabetLen = alphabet.length();
|
||||
|
||||
do {
|
||||
final int index = (int) (input % alphabetLen);
|
||||
if (index >= 0 && index < alphabet.length()) {
|
||||
hash = alphabet.charAt(index) + hash;
|
||||
}
|
||||
input /= alphabetLen;
|
||||
} while (input > 0);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
private static Long unhash(String input, String alphabet) {
|
||||
long number = 0, pos;
|
||||
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
pos = alphabet.indexOf(input.charAt(i));
|
||||
number = number * alphabet.length() + pos;
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Hashid algorithm version.
|
||||
*
|
||||
* @return Hashids algorithm version implemented.
|
||||
*/
|
||||
public String getVersion() {
|
||||
return "1.0.0";
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.common.utils;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
|
||||
/**
|
||||
* 唯一字符串生成器
|
||||
* 参考 <a href="https://huzb.me/2018/03/23/%E7%AE%80%E5%8D%95%E7%9A%84%E5%AF%86%E7%A0%81%E5%AD%A6%E7%94%9F%E6%88%90%E5%94%AF%E4%B8%80%E9%82%80%E8%AF%B7%E7%A0%81">...</a>
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022年07月11日 14:07
|
||||
*/
|
||||
public final class UniqueStrGeneratorUtils {
|
||||
|
||||
private static final String BEGIN_SALT = "opsli_2022_begin";
|
||||
private static final String END_SALT = "opsli_2022_end";
|
||||
private static final String CONTENT_SALT = "opsli_2022";
|
||||
|
||||
private static final HashIdsUtil DISTURB_HASH_ID_BEGIN = new HashIdsUtil(BEGIN_SALT, 2);
|
||||
private static final HashIdsUtil DISTURB_HASH_ID_END = new HashIdsUtil(END_SALT, 3);
|
||||
private static final HashIdsUtil CONTENT_HASH_ID = new HashIdsUtil(CONTENT_SALT, 7);
|
||||
|
||||
|
||||
/** 生成 */
|
||||
public static String generator(long i) {
|
||||
int begin = RandomUtil.randomInt(0, 10);
|
||||
int end = RandomUtil.randomInt(10, 100);
|
||||
String disturbHashIdBeginStr = DISTURB_HASH_ID_BEGIN.encode(begin);
|
||||
String disturbHashIdEndStr = DISTURB_HASH_ID_END.encode(end);
|
||||
String contentHashIdStr = CONTENT_HASH_ID.encode(i);
|
||||
return disturbHashIdBeginStr + contentHashIdStr + disturbHashIdEndStr;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.autoconfigure.conf;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Cors 跨域处理
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022年07月14日12:57:33
|
||||
**/
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 解决跨域问题
|
||||
*
|
||||
* @param registry registry
|
||||
*/
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true)
|
||||
.maxAge(7200);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.autoconfigure.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Token配置
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2020-09-15
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = TokenProperties.PROP_PREFIX)
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TokenProperties {
|
||||
|
||||
public static final String PROP_PREFIX = "opsli.auth.token";
|
||||
|
||||
/** 盐 */
|
||||
private String secret;
|
||||
|
||||
/** 有效时间 */
|
||||
private int effectiveTime;
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.eventbus;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* EventBus 与 Spring 打通桥梁
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2021年12月7日10:39:16
|
||||
*/
|
||||
public abstract class AbstractSpringEventBus implements IEventBus, ApplicationContextAware {
|
||||
private ApplicationContext context;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.context = applicationContext;
|
||||
this.scanConsumer(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanConsumer(String packageName) {
|
||||
context.getBeansOfType(IEventConsumer.class).forEach((k,v)->{
|
||||
this.addConsumer(v);
|
||||
});
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.filters.aspect;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.TimeInterval;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.opsli.core.utils.LogUtil;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static org.opsli.common.constants.OrderConstants.TOKEN_AOP_SORT;
|
||||
|
||||
/**
|
||||
* 日志 拦截处理
|
||||
*
|
||||
* @author parker
|
||||
* @date 2020-09-16
|
||||
*/
|
||||
@Slf4j
|
||||
@Order(TOKEN_AOP_SORT)
|
||||
@Aspect
|
||||
@Component
|
||||
public class LogAop {
|
||||
|
||||
|
||||
@Pointcut("execution(public * org.opsli..*.*Controller*.*(..))")
|
||||
public void requestMapping() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 切如 post 请求
|
||||
* @param point point
|
||||
*/
|
||||
@Around("requestMapping()")
|
||||
public Object tokenAop(ProceedingJoinPoint point) throws Throwable {
|
||||
// 计时器
|
||||
TimeInterval timer = DateUtil.timer();
|
||||
// 执行
|
||||
Exception exception = null;
|
||||
// 防止线程抛异常 线程变量不回收 导致oom
|
||||
Object returnValue;
|
||||
try {
|
||||
// 执行正常操作
|
||||
returnValue = point.proceed();
|
||||
}catch (Exception e){
|
||||
exception = e;
|
||||
throw e;
|
||||
} finally {
|
||||
// 花费毫秒数
|
||||
long timerCount = timer.interval();
|
||||
//保存日志
|
||||
LogUtil.saveLog(point, exception, timerCount);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.log.bean;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 操作日志
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2021年7月15日20:28:24
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
public class OperationLog implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 日志ID */
|
||||
private String id;
|
||||
|
||||
/** 创建时间 */
|
||||
private long createTime;
|
||||
|
||||
/** 日志等级 */
|
||||
private String level;
|
||||
|
||||
/** 被操作的系统模块 */
|
||||
private String moduleId;
|
||||
|
||||
/** 方法名 */
|
||||
private String method;
|
||||
|
||||
/** 参数 */
|
||||
private String args;
|
||||
|
||||
/** 操作人id */
|
||||
private String userId;
|
||||
|
||||
/** 操作人用户名 */
|
||||
private String username;
|
||||
|
||||
/** 真实姓名 */
|
||||
private String realName;
|
||||
|
||||
/** 日志描述 */
|
||||
private String description;
|
||||
|
||||
/** 操作类型 */
|
||||
private String operationType;
|
||||
|
||||
/** 方法运行时间 */
|
||||
private Long runTime;
|
||||
|
||||
/** 方法返回值 */
|
||||
private String returnValue;
|
||||
|
||||
/** 租户ID */
|
||||
private String tenantId;
|
||||
|
||||
/**
|
||||
* 日志请求类型
|
||||
*/
|
||||
private String logType;
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.log.bus;
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.SubscriberExceptionContext;
|
||||
import com.google.common.eventbus.SubscriberExceptionHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.opsli.common.thread.ThreadPoolFactory;
|
||||
import org.opsli.core.eventbus.AbstractSpringEventBus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 操作日志事件 总线
|
||||
* @author 周鹏程
|
||||
* @date 2021年12月7日10:42:39
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class OperationLogEventBus extends AbstractSpringEventBus implements SubscriberExceptionHandler {
|
||||
|
||||
private final EventBus eventBus;
|
||||
|
||||
public OperationLogEventBus() {
|
||||
// 异步事件配置线程池
|
||||
eventBus = new AsyncEventBus(
|
||||
ThreadPoolFactory.createInitThreadPool(5, 10, 60, TimeUnit.SECONDS,
|
||||
1024, "Operation-Log-Event-Bus",
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
)
|
||||
, this
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void post(Object event) {
|
||||
eventBus.post(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addConsumer(Object obj) {
|
||||
eventBus.register(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConsumer(Object obj) {
|
||||
eventBus.unregister(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleException(Throwable exception, SubscriberExceptionContext context) {
|
||||
log.error("user event handler exception", exception);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.log.enums;
|
||||
|
||||
|
||||
/**
|
||||
* 模块字典
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2021年7月15日20:19:27
|
||||
*/
|
||||
public enum ModuleEnum {
|
||||
|
||||
/** 模块 */
|
||||
MODULE_UNKNOWN("-1", "未知(请配置模块)"),
|
||||
MODULE_COMMON("00", "公共模块"),
|
||||
MODULE_USER("01", "用户模块"),
|
||||
MODULE_ROLE("02", "角色模块"),
|
||||
MODULE_MENU("03", "菜单模块"),
|
||||
MODULE_ORG("04", "组织模块"),
|
||||
MODULE_DICT("05", "字典模块"),
|
||||
MODULE_TENANT("06", "租户模块"),
|
||||
MODULE_AREA("07", "地区模块"),
|
||||
MODULE_MONITOR("08", "监控模块"),
|
||||
MODULE_GENERATOR("09", "代码生成器"),
|
||||
|
||||
MODULE_OPERATION("11", "行为日志"),
|
||||
|
||||
|
||||
MODULE_TEST("100", "测试模块"),
|
||||
MODULE_TEST_USER("101", "测试用户模块"),
|
||||
MODULE_TEST_CAR("102", "测试汽车模块"),
|
||||
|
||||
|
||||
;
|
||||
|
||||
private final String id;
|
||||
private final String name;
|
||||
|
||||
ModuleEnum(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.log.enums;
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2021年7月15日20:27:27
|
||||
*/
|
||||
public enum OperationTypeEnum {
|
||||
/**
|
||||
* 操作类型
|
||||
*/
|
||||
UNKNOWN("unknown"),
|
||||
DELETE("delete"),
|
||||
SELECT("select"),
|
||||
UPDATE("update"),
|
||||
INSERT("insert");
|
||||
|
||||
private String value;
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
OperationTypeEnum(String s) {
|
||||
this.value = s;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.security.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.opsli.api.wrapper.system.user.UserModel;
|
||||
import org.opsli.common.enums.DictType;
|
||||
import org.opsli.core.utils.UserUtil;
|
||||
import org.opsli.plugins.security.UserDetailModel;
|
||||
import org.opsli.plugins.security.authentication.EmailCodeAuthenticationToken;
|
||||
import org.opsli.plugins.security.authentication.EmailPasswordAuthenticationToken;
|
||||
import org.opsli.plugins.security.properties.AuthProperties;
|
||||
import org.opsli.plugins.security.service.ILoadUserDetailService;
|
||||
import org.opsli.plugins.security.utils.PasswordUtil;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 邮箱+验证码 获取用户信息Service
|
||||
* 实际只需要用到 邮箱唯一主键即可
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-14 4:44 PM
|
||||
**/
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class EmailUserDetailDetailServiceImpl implements ILoadUserDetailService {
|
||||
|
||||
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||
private final AuthProperties authProperties;
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends Authentication>> getClassTypes() {
|
||||
List<Class<? extends Authentication>> classes = new ArrayList<>();
|
||||
classes.add(EmailCodeAuthenticationToken.class);
|
||||
classes.add(EmailPasswordAuthenticationToken.class);
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UserDetails> loadUserByPrincipal(Object principal) {
|
||||
UserModel userModel = UserUtil.getUserByEmail((String) principal);
|
||||
if(null == userModel){
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// 处理权限数据
|
||||
List<String> authorities = new ArrayList<>();
|
||||
List<String> userRoles = UserUtil.getUserRolesByUserId(userModel.getId());
|
||||
List<String> userAllPerms = UserUtil.getUserAllPermsByUserId(userModel.getId());
|
||||
for (String userRole : userRoles) {
|
||||
authorities.add(DEFAULT_ROLE_PREFIX+userRole);
|
||||
}
|
||||
authorities.addAll(userAllPerms);
|
||||
|
||||
// 清除空的 授权
|
||||
authorities.removeIf(StrUtil::isEmpty);
|
||||
|
||||
List<GrantedAuthority> grantedAuthorities = authorities.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UserDetailModel userDetailModel = UserDetailModel.builder()
|
||||
.username(userModel.getUsername())
|
||||
.password(userModel.getPassword())
|
||||
// 账户启动
|
||||
.enabled(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未过期(如果需要 请自行扩展字段)
|
||||
.accountNonExpired(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未锁定(如果需要 请自行扩展字段)
|
||||
.accountNonLocked(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 判断凭证是否过期(默认不判断 如果需要 请自行扩展过期后修改密码操作)
|
||||
.credentialsNonExpired(
|
||||
PasswordUtil.isCredentialsNonExpired(
|
||||
userModel.getPassword(), authProperties.getCredentialsExpired()))
|
||||
// 授权信息
|
||||
.authorities(grantedAuthorities)
|
||||
.build();
|
||||
|
||||
return Optional.ofNullable(userDetailModel);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.security.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.opsli.api.wrapper.system.user.UserModel;
|
||||
import org.opsli.common.enums.DictType;
|
||||
import org.opsli.core.utils.UserUtil;
|
||||
import org.opsli.plugins.security.UserDetailModel;
|
||||
import org.opsli.plugins.security.authentication.MobileCodeAuthenticationToken;
|
||||
import org.opsli.plugins.security.authentication.MobilePasswordAuthenticationToken;
|
||||
import org.opsli.plugins.security.properties.AuthProperties;
|
||||
import org.opsli.plugins.security.service.ILoadUserDetailService;
|
||||
import org.opsli.plugins.security.utils.PasswordUtil;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 手机号+验证码 获取用户信息Service
|
||||
* 实际只需要用到 手机号唯一主键即可
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-14 4:44 PM
|
||||
**/
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class MobileUserDetailDetailServiceImpl implements ILoadUserDetailService {
|
||||
|
||||
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||
private final AuthProperties authProperties;
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends Authentication>> getClassTypes() {
|
||||
List<Class<? extends Authentication>> classes = new ArrayList<>();
|
||||
classes.add(MobileCodeAuthenticationToken.class);
|
||||
classes.add(MobilePasswordAuthenticationToken.class);
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UserDetails> loadUserByPrincipal(Object principal) {
|
||||
UserModel userModel = UserUtil.getUserByMobile((String) principal);
|
||||
if(null == userModel){
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// 处理权限数据
|
||||
List<String> authorities = new ArrayList<>();
|
||||
List<String> userRoles = UserUtil.getUserRolesByUserId(userModel.getId());
|
||||
List<String> userAllPerms = UserUtil.getUserAllPermsByUserId(userModel.getId());
|
||||
for (String userRole : userRoles) {
|
||||
authorities.add(DEFAULT_ROLE_PREFIX+userRole);
|
||||
}
|
||||
authorities.addAll(userAllPerms);
|
||||
|
||||
// 清除空的 授权
|
||||
authorities.removeIf(StrUtil::isEmpty);
|
||||
|
||||
List<GrantedAuthority> grantedAuthorities = authorities.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UserDetailModel userDetailModel = UserDetailModel.builder()
|
||||
.username(userModel.getUsername())
|
||||
.password(userModel.getPassword())
|
||||
// 账户启动
|
||||
.enabled(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未过期(如果需要 请自行扩展字段)
|
||||
.accountNonExpired(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未锁定(如果需要 请自行扩展字段)
|
||||
.accountNonLocked(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 判断凭证是否过期(默认不判断 如果需要 请自行扩展过期后修改密码操作)
|
||||
.credentialsNonExpired(
|
||||
PasswordUtil.isCredentialsNonExpired(
|
||||
userModel.getPassword(), authProperties.getCredentialsExpired()))
|
||||
// 授权信息
|
||||
.authorities(grantedAuthorities)
|
||||
.build();
|
||||
|
||||
return Optional.ofNullable(userDetailModel);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.security.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.opsli.api.wrapper.system.user.UserModel;
|
||||
import org.opsli.common.enums.DictType;
|
||||
import org.opsli.core.utils.UserUtil;
|
||||
import org.opsli.plugins.security.UserDetailModel;
|
||||
import org.opsli.plugins.security.properties.AuthProperties;
|
||||
import org.opsli.plugins.security.service.ILoadUserDetailService;
|
||||
import org.opsli.plugins.security.utils.PasswordUtil;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户ID 获取用户信息Service
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-14 4:44 PM
|
||||
**/
|
||||
@AllArgsConstructor
|
||||
@Service("uidUserDetailDetailService")
|
||||
public class UidUserDetailDetailServiceImpl implements ILoadUserDetailService {
|
||||
|
||||
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||
private final AuthProperties authProperties;
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends Authentication>> getClassTypes() {
|
||||
// 为空则不会进入 Token 工厂内
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UserDetails> loadUserByPrincipal(Object principal) {
|
||||
UserModel userModel = UserUtil.getUser((String) principal);
|
||||
if(null == userModel){
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// 处理权限数据
|
||||
List<String> authorities = new ArrayList<>();
|
||||
List<String> userRoles = UserUtil.getUserRolesByUserId(userModel.getId());
|
||||
List<String> userAllPerms = UserUtil.getUserAllPermsByUserId(userModel.getId());
|
||||
for (String userRole : userRoles) {
|
||||
authorities.add(DEFAULT_ROLE_PREFIX+userRole);
|
||||
}
|
||||
authorities.addAll(userAllPerms);
|
||||
|
||||
// 清除空的 授权
|
||||
authorities.removeIf(StrUtil::isEmpty);
|
||||
|
||||
List<GrantedAuthority> grantedAuthorities = authorities.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UserDetailModel userDetailModel = UserDetailModel.builder()
|
||||
.username(userModel.getUsername())
|
||||
.password(userModel.getPassword())
|
||||
// 账户启动
|
||||
.enabled(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未过期(如果需要 请自行扩展字段)
|
||||
.accountNonExpired(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未锁定(如果需要 请自行扩展字段)
|
||||
.accountNonLocked(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 判断凭证是否过期(默认不判断 如果需要 请自行扩展过期后修改密码操作)
|
||||
.credentialsNonExpired(
|
||||
PasswordUtil.isCredentialsNonExpired(
|
||||
userModel.getPassword(), authProperties.getCredentialsExpired()))
|
||||
// 授权信息
|
||||
.authorities(grantedAuthorities)
|
||||
.build();
|
||||
|
||||
return Optional.ofNullable(userDetailModel);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright 2020 OPSLI 快速开发平台 https://www.opsli.com
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
package org.opsli.core.security.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.opsli.api.wrapper.system.user.UserModel;
|
||||
import org.opsli.common.enums.DictType;
|
||||
import org.opsli.core.utils.UserUtil;
|
||||
import org.opsli.plugins.security.UserDetailModel;
|
||||
import org.opsli.plugins.security.properties.AuthProperties;
|
||||
import org.opsli.plugins.security.service.ILoadUserDetailService;
|
||||
import org.opsli.plugins.security.utils.PasswordUtil;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 用户名+密码 获取用户信息Service
|
||||
* 实际只需要用到 用户名唯一主键即可
|
||||
*
|
||||
* @author Parker
|
||||
* @date 2022-07-14 4:44 PM
|
||||
**/
|
||||
@AllArgsConstructor
|
||||
@Service("usernameUserDetailDetailService")
|
||||
public class UsernameUserDetailDetailServiceImpl implements ILoadUserDetailService {
|
||||
|
||||
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
|
||||
private final AuthProperties authProperties;
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends Authentication>> getClassTypes() {
|
||||
return Collections.singleton(UsernamePasswordAuthenticationToken.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<UserDetails> loadUserByPrincipal(Object principal) {
|
||||
UserModel userModel = UserUtil.getUserByUserName((String) principal);
|
||||
if(null == userModel){
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// 处理权限数据
|
||||
List<String> authorities = new ArrayList<>();
|
||||
List<String> userRoles = UserUtil.getUserRolesByUserId(userModel.getId());
|
||||
List<String> userAllPerms = UserUtil.getUserAllPermsByUserId(userModel.getId());
|
||||
for (String userRole : userRoles) {
|
||||
authorities.add(DEFAULT_ROLE_PREFIX+userRole);
|
||||
}
|
||||
authorities.addAll(userAllPerms);
|
||||
|
||||
// 清除空的 授权
|
||||
authorities.removeIf(StrUtil::isEmpty);
|
||||
|
||||
List<GrantedAuthority> grantedAuthorities = authorities.stream()
|
||||
.map(SimpleGrantedAuthority::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
UserDetailModel userDetailModel = UserDetailModel.builder()
|
||||
.username(userModel.getUsername())
|
||||
.password(userModel.getPassword())
|
||||
// 账户启动
|
||||
.enabled(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未过期(如果需要 请自行扩展字段)
|
||||
.accountNonExpired(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 账户未锁定(如果需要 请自行扩展字段)
|
||||
.accountNonLocked(
|
||||
DictType.NO_YES_YES.getValue().equals(userModel.getEnable()))
|
||||
// 判断凭证是否过期(默认不判断 如果需要 请自行扩展过期后修改密码操作)
|
||||
.credentialsNonExpired(
|
||||
PasswordUtil.isCredentialsNonExpired(
|
||||
userModel.getPassword(), authProperties.getCredentialsExpired()))
|
||||
// 授权信息
|
||||
.authorities(grantedAuthorities)
|
||||
.build();
|
||||
|
||||
return Optional.ofNullable(userDetailModel);
|
||||
}
|
||||
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
package org.opsli.core.security.shiro.cache;
|
||||
|
||||
|
||||
import org.apache.shiro.cache.Cache;
|
||||
import org.apache.shiro.cache.CacheException;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.util.CollectionUtils;
|
||||
import org.opsli.core.security.shiro.exception.PrincipalIdNullException;
|
||||
import org.opsli.core.security.shiro.exception.PrincipalInstanceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author: sunzhiqiang
|
||||
* @date: 2018/6/22
|
||||
* @description: 参考 shiro-redis 开源项目 Git地址 https://github.com/alexxiyang/shiro-redis
|
||||
*/
|
||||
public class RedisCache<K, V> implements Cache<K, V> {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(RedisCache.class);
|
||||
|
||||
private RedisManager redisManager;
|
||||
private String keyPrefix = "";
|
||||
private int expire = 0;
|
||||
private String principalIdFieldName = RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME;
|
||||
|
||||
/**
|
||||
* Construction
|
||||
* @param redisManager
|
||||
*/
|
||||
public RedisCache(RedisManager redisManager, String prefix, int expire, String principalIdFieldName) {
|
||||
if (redisManager == null) {
|
||||
throw new IllegalArgumentException("redisManager cannot be null.");
|
||||
}
|
||||
this.redisManager = redisManager;
|
||||
if (prefix != null && !"".equals(prefix)) {
|
||||
this.keyPrefix = prefix;
|
||||
}
|
||||
if (expire != -1) {
|
||||
this.expire = expire;
|
||||
}
|
||||
if (principalIdFieldName != null && !"".equals(principalIdFieldName)) {
|
||||
this.principalIdFieldName = principalIdFieldName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) throws CacheException {
|
||||
logger.debug("get key [{}]",key);
|
||||
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
String redisCacheKey = getRedisCacheKey(key);
|
||||
Object rawValue = redisManager.get(redisCacheKey);
|
||||
if (rawValue == null) {
|
||||
return null;
|
||||
}
|
||||
V value = (V) rawValue;
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
throw new CacheException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) throws CacheException {
|
||||
logger.debug("put key [{}]",key);
|
||||
if (key == null) {
|
||||
logger.warn("Saving a null key is meaningless, return value directly without call Redis.");
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
String redisCacheKey = getRedisCacheKey(key);
|
||||
redisManager.set(redisCacheKey, value != null ? value : null, expire);
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
throw new CacheException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) throws CacheException {
|
||||
logger.debug("remove key [{}]",key);
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String redisCacheKey = getRedisCacheKey(key);
|
||||
Object rawValue = redisManager.get(redisCacheKey);
|
||||
V previous = (V) rawValue;
|
||||
redisManager.del(redisCacheKey);
|
||||
return previous;
|
||||
} catch (Exception e) {
|
||||
throw new CacheException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getRedisCacheKey(K key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
return this.keyPrefix + getStringRedisKey(key);
|
||||
}
|
||||
|
||||
private String getStringRedisKey(K key) {
|
||||
String redisKey;
|
||||
if (key instanceof PrincipalCollection) {
|
||||
redisKey = getRedisKeyFromPrincipalIdField((PrincipalCollection) key);
|
||||
} else {
|
||||
redisKey = key.toString();
|
||||
}
|
||||
return redisKey;
|
||||
}
|
||||
|
||||
private String getRedisKeyFromPrincipalIdField(PrincipalCollection key) {
|
||||
String redisKey;
|
||||
Object principalObject = key.getPrimaryPrincipal();
|
||||
Method pincipalIdGetter = null;
|
||||
Method[] methods = principalObject.getClass().getDeclaredMethods();
|
||||
for (Method m:methods) {
|
||||
if (RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME.equals(this.principalIdFieldName)
|
||||
&& ("getAuthCacheKey".equals(m.getName()) || "getId".equals(m.getName()))) {
|
||||
pincipalIdGetter = m;
|
||||
break;
|
||||
}
|
||||
if (m.getName().equals("get" + this.principalIdFieldName.substring(0, 1).toUpperCase() + this.principalIdFieldName.substring(1))) {
|
||||
pincipalIdGetter = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pincipalIdGetter == null) {
|
||||
throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName);
|
||||
}
|
||||
|
||||
try {
|
||||
Object idObj = pincipalIdGetter.invoke(principalObject);
|
||||
if (idObj == null) {
|
||||
throw new PrincipalIdNullException(principalObject.getClass(), this.principalIdFieldName);
|
||||
}
|
||||
redisKey = idObj.toString();
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName, e);
|
||||
}
|
||||
|
||||
return redisKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() throws CacheException {
|
||||
logger.debug("clear cache");
|
||||
Set<String> keys = null;
|
||||
try {
|
||||
keys = redisManager.scan(this.keyPrefix + "*");
|
||||
} catch (Exception e) {
|
||||
logger.error("get keys error", e);
|
||||
}
|
||||
if (keys == null || keys.size() == 0) {
|
||||
return;
|
||||
}
|
||||
for (String key: keys) {
|
||||
redisManager.del(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
Long longSize = 0L;
|
||||
try {
|
||||
longSize = new Long(redisManager.scanSize(this.keyPrefix + "*"));
|
||||
} catch (Exception e) {
|
||||
logger.error("get keys error", e);
|
||||
}
|
||||
return longSize.intValue();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Set<K> keys() {
|
||||
Set<String> keys = null;
|
||||
try {
|
||||
keys = redisManager.scan(this.keyPrefix + "*");
|
||||
} catch (Exception e) {
|
||||
logger.error("get keys error", e);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(keys)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<K> convertedKeys = new HashSet<K>();
|
||||
for (String key:keys) {
|
||||
try {
|
||||
convertedKeys.add((K) key);
|
||||
} catch (Exception e) {
|
||||
logger.error("deserialize keys error", e);
|
||||
}
|
||||
}
|
||||
return convertedKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
Set<String> keys = null;
|
||||
try {
|
||||
keys = redisManager.scan(this.keyPrefix + "*");
|
||||
} catch (Exception e) {
|
||||
logger.error("get values error", e);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(keys)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
List<V> values = new ArrayList<V>(keys.size());
|
||||
for (String key : keys) {
|
||||
V value = null;
|
||||
try {
|
||||
value = (V) redisManager.get(key);
|
||||
} catch (Exception e) {
|
||||
logger.error("deserialize values= error", e);
|
||||
}
|
||||
if (value != null) {
|
||||
values.add(value);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(values);
|
||||
}
|
||||
|
||||
public String getKeyPrefix() {
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
public void setKeyPrefix(String keyPrefix) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
public String getPrincipalIdFieldName() {
|
||||
return principalIdFieldName;
|
||||
}
|
||||
|
||||
public void setPrincipalIdFieldName(String principalIdFieldName) {
|
||||
this.principalIdFieldName = principalIdFieldName;
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package org.opsli.core.security.shiro.cache;
|
||||
|
||||
import org.apache.shiro.cache.Cache;
|
||||
import org.apache.shiro.cache.CacheException;
|
||||
import org.apache.shiro.cache.CacheManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @description: 参考 shiro-redis 开源项目 Git地址 https://github.com/alexxiyang/shiro-redis
|
||||
*/
|
||||
public class RedisCacheManager implements CacheManager {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RedisCacheManager.class);
|
||||
|
||||
/**
|
||||
* fast lookup by name map
|
||||
*/
|
||||
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
|
||||
|
||||
private RedisManager redisManager;
|
||||
|
||||
/**
|
||||
* expire time in seconds
|
||||
*/
|
||||
private static final int DEFAULT_EXPIRE = 1800;
|
||||
private int expire = DEFAULT_EXPIRE;
|
||||
|
||||
/**
|
||||
* The Redis key prefix for caches
|
||||
*/
|
||||
public static final String DEFAULT_CACHE_KEY_PREFIX = "shiro:cache:";
|
||||
private String keyPrefix = DEFAULT_CACHE_KEY_PREFIX;
|
||||
|
||||
public static final String DEFAULT_PRINCIPAL_ID_FIELD_NAME = "authCacheKey or id";
|
||||
private String principalIdFieldName = DEFAULT_PRINCIPAL_ID_FIELD_NAME;
|
||||
|
||||
@Override
|
||||
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
|
||||
logger.debug("get cache, name={}",name);
|
||||
|
||||
Cache cache = caches.get(name);
|
||||
|
||||
if (cache == null) {
|
||||
cache = new RedisCache<K, V>(redisManager,keyPrefix + name + ":", expire, principalIdFieldName);
|
||||
caches.put(name, cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
public RedisManager getRedisManager() {
|
||||
return redisManager;
|
||||
}
|
||||
|
||||
public void setRedisManager(RedisManager redisManager) {
|
||||
this.redisManager = redisManager;
|
||||
}
|
||||
|
||||
public String getKeyPrefix() {
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
public void setKeyPrefix(String keyPrefix) {
|
||||
this.keyPrefix = keyPrefix;
|
||||
}
|
||||
|
||||
public int getExpire() {
|
||||
return expire;
|
||||
}
|
||||
|
||||
public void setExpire(int expire) {
|
||||
this.expire = expire;
|
||||
}
|
||||
|
||||
public String getPrincipalIdFieldName() {
|
||||
return principalIdFieldName;
|
||||
}
|
||||
|
||||
public void setPrincipalIdFieldName(String principalIdFieldName) {
|
||||
this.principalIdFieldName = principalIdFieldName;
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
package org.opsli.core.security.shiro.cache;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sunzhiqiang
|
||||
* 基于spring和redis的redisTemplate工具类
|
||||
*/
|
||||
public class RedisManager {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
//=============================common============================
|
||||
/**
|
||||
* 指定缓存失效时间
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
*/
|
||||
public void expire(String key,long time){
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断key是否存在
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean hasKey(String key){
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void del(String ... key){
|
||||
if(key!=null&&key.length>0){
|
||||
if(key.length==1){
|
||||
redisTemplate.delete(key[0]);
|
||||
}else{
|
||||
redisTemplate.delete(CollectionUtils.arrayToList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除key
|
||||
* @param keys
|
||||
*/
|
||||
public void del(Collection keys){
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
//============================String=============================
|
||||
/**
|
||||
* 普通缓存获取
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key){
|
||||
return redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public void set(String key,Object value) {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入并设置时间
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||
*/
|
||||
public void set(String key,Object value,long time){
|
||||
if(time>0){
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
}else{
|
||||
set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用scan命令 查询某些前缀的key
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Set<String> scan(String key){
|
||||
Set<String> execute = this.redisTemplate.execute(new RedisCallback<Set<String>>() {
|
||||
|
||||
@Override
|
||||
public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
|
||||
Set<String> binaryKeys = new HashSet<>();
|
||||
|
||||
Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(key).count(1000).build());
|
||||
while (cursor.hasNext()) {
|
||||
binaryKeys.add(new String(cursor.next()));
|
||||
}
|
||||
return binaryKeys;
|
||||
}
|
||||
});
|
||||
return execute;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用scan命令 查询某些前缀的key 有多少个
|
||||
* 用来获取当前session数量,也就是在线用户
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public Long scanSize(String key){
|
||||
long dbSize = this.redisTemplate.execute(new RedisCallback<Long>() {
|
||||
|
||||
@Override
|
||||
public Long doInRedis(RedisConnection connection) throws DataAccessException {
|
||||
long count = 0L;
|
||||
Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(1000).build());
|
||||
while (cursor.hasNext()) {
|
||||
cursor.next();
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
});
|
||||
return dbSize;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue