新增: 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package org.opsli.core.security.shiro.cache.serializer;
|
|
||||||
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
|
||||||
import org.springframework.data.redis.serializer.SerializationException;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>序列化工具类</p>
|
|
||||||
*
|
|
||||||
* @author sunzhiqiang23
|
|
||||||
* @date 2020-04-27 19:48
|
|
||||||
*/
|
|
||||||
public class SerializeUtils implements RedisSerializer {
|
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class);
|
|
||||||
|
|
||||||
public static boolean isEmpty(byte[] data) {
|
|
||||||
return (data == null || data.length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 序列化
|
|
||||||
* @param object
|
|
||||||
* @return
|
|
||||||
* @throws SerializationException
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public byte[] serialize(Object object) throws SerializationException {
|
|
||||||
byte[] result = null;
|
|
||||||
|
|
||||||
if (object == null) {
|
|
||||||
return new byte[0];
|
|
||||||
}
|
|
||||||
try (
|
|
||||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(128);
|
|
||||||
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream)
|
|
||||||
){
|
|
||||||
|
|
||||||
if (!(object instanceof Serializable)) {
|
|
||||||
throw new IllegalArgumentException(SerializeUtils.class.getSimpleName() + " requires a Serializable payload " +
|
|
||||||
"but received an object of type [" + object.getClass().getName() + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
objectOutputStream.writeObject(object);
|
|
||||||
objectOutputStream.flush();
|
|
||||||
result = byteStream.toByteArray();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.error("Failed to serialize",ex);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 反序列化
|
|
||||||
* @param bytes
|
|
||||||
* @return
|
|
||||||
* @throws SerializationException
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object deserialize(byte[] bytes) throws SerializationException {
|
|
||||||
|
|
||||||
Object result = null;
|
|
||||||
|
|
||||||
if (isEmpty(bytes)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try (
|
|
||||||
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
|
|
||||||
ObjectInputStream objectInputStream = new ObjectInputStream(byteStream)
|
|
||||||
){
|
|
||||||
result = objectInputStream.readObject();
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Failed to deserialize",e);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue