This commit is contained in:
六如
2024-10-04 21:48:38 +08:00
parent 00da3cc0a9
commit c08fec74c9
987 changed files with 24735 additions and 22137 deletions

33
sop-admin/sop-admin-backend/.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

View File

@@ -0,0 +1,294 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-admin-backend</artifactId>
<version>5.0.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- springboot 版本-->
<spring-boot.version>2.6.15</spring-boot.version>
<!-- spring cloud 版本 -->
<spring-cloud.version>2021.0.5</spring-cloud.version>
<!-- spring cloud alibaba 版本 -->
<!-- 具体版本对应关系见https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E -->
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
<!-- dubbo版本 -->
<dubbo.version>3.2.10</dubbo.version>
<junit.version>4.11</junit.version>
<commons-io.version>2.5</commons-io.version>
<commons-fileupload.version>1.3.3</commons-fileupload.version>
<commons-collection.version>3.2.2</commons-collection.version>
<commons-lang3.version>3.8.1</commons-lang3.version>
<commons-codec.version>1.11</commons-codec.version>
<commons-logging.version>1.2</commons-logging.version>
<validation-api.version>2.0.1.Final</validation-api.version>
<hibernate-validator.version>6.0.13.Final</hibernate-validator.version>
<fastmybatis.version>3.0.10</fastmybatis.version>
</properties>
<dependencies>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-support</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!-- nacos注册中心 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-nacos-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.gitee.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- provided -->
<!-- 仅在开发中使用 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- 加了这个就不需要加版本号了 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.gitee.durcframework</groupId>
<artifactId>fastmybatis-spring-boot-starter</artifactId>
<version>${fastmybatis.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.7</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${validation-api.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
</dependency>
<!-- commons -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collection.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>aliyun</id>
<name>aliyun</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 打包时跳过测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.adminbackend;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class SopAdminBackendApplication {
public static void main(String[] args) {
SpringApplication.run(SopAdminBackendApplication.class, args);
}
}

View File

@@ -0,0 +1,22 @@
package com.gitee.sop.adminbackend.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author 六如
*/
@AllArgsConstructor
@Getter
public enum ConfigKeyEnum {
PASSWORD_SALT("admin.password-salt", ""),
JWT_TIMEOUT_DAYS("admin.jwt-timeout-days", "365"),
JWT_SECRET("admin.jwt.secret", ""),
;
private final String key;
private final String defaultValue;
}

View File

@@ -0,0 +1,9 @@
package com.gitee.sop.adminbackend.common;
public interface IConfig {
String getConfig(String key);
String getConfig(String key, String defaultValue);
}

View File

@@ -0,0 +1,46 @@
package com.gitee.sop.adminbackend.common;
import lombok.Data;
import java.util.Objects;
/**
* @author thc
*/
@Data
public class Result<T> {
private static final Result<?> RESULT = new Result<>();
private String code = "0";
private T data;
private String msg = "success";
public static Result<?> ok() {
return RESULT;
}
public static <E> Result<E> ok(E obj) {
Result<E> result = new Result<>();
result.setData(obj);
return result;
}
public static <E> Result<E> err(String msg) {
Result<E> result = new Result<>();
result.setCode("1");
result.setMsg(msg);
return result;
}
public static <E> Result<E> err(String code, String msg) {
Result<E> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
return result;
}
public boolean getSuccess() {
return Objects.equals("0", code);
}
}

View File

@@ -0,0 +1,32 @@
package com.gitee.sop.adminbackend.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
/**
* @author 六如
*/
public class SpringContext {
private static ApplicationContext ctx;
public static <T> T getBean(Class<T> clazz) {
return ctx.getBean(clazz);
}
public static Object getBean(String beanName) {
return ctx.getBean(beanName);
}
public static void setApplicationContext(ApplicationContext ctx) {
SpringContext.ctx = ctx;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
public static void publishEvent(ApplicationEvent event) {
ctx.publishEvent(event);
}
}

View File

@@ -0,0 +1,24 @@
package com.gitee.sop.adminbackend.common;
/**
* 登录用户信息
* @author 六如
*/
public interface User {
/**
* 用户id
* @return
*/
Long getUserId();
/**
* 昵称
* @return
*/
String getNickname();
Integer getStatus();
String getToken();
}

View File

@@ -0,0 +1,10 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class BizException extends RuntimeException {
public BizException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,30 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public enum ErrorCode {
// 1000: 登录失败
LOGIN_FAIL("1000", "login error"),
JWT_CREATE("1000", "create token error"),
JWT_ERROR("1000", "invalid token"),
JWT_EXPIRED("1000", "token expired"),
SET_PASSWORD("2000", "set password"),
;
ErrorCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
private final String code;
private final String msg;
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}

View File

@@ -0,0 +1,7 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class ErrorTokenException extends Exception {
}

View File

@@ -0,0 +1,8 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public interface ExceptionCode {
ErrorCode getCode();
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class JwtCreateException extends RuntimeException implements ExceptionCode {
@Override
public ErrorCode getCode() {
return ErrorCode.JWT_CREATE;
}
}

View File

@@ -0,0 +1,12 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class JwtErrorException extends Exception {
@Override
public String getMessage() {
return "jwt verify error";
}
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class JwtExpiredException extends Exception {
@Override
public String getMessage() {
return "jwt expired";
}
}

View File

@@ -0,0 +1,18 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class LoginFailureException extends RuntimeException implements ExceptionCode {
@Override
public ErrorCode getCode() {
return ErrorCode.LOGIN_FAIL;
}
public LoginFailureException(String message) {
super(message);
}
public LoginFailureException() {
}
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.adminbackend.common.exception;
/**
* @author tanghc
*/
public class SetPasswordException extends RuntimeException implements ExceptionCode {
@Override
public ErrorCode getCode() {
return ErrorCode.SET_PASSWORD;
}
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.adminbackend.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author 六如
*/
@Configuration
@ConfigurationProperties(prefix = "admin")
@Data
public class AdminConfig {
private int jwtTimeoutDays;
}

View File

@@ -0,0 +1,46 @@
package com.gitee.sop.adminbackend.config;
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
import com.gitee.sop.adminbackend.common.IConfig;
import com.gitee.sop.adminbackend.common.SpringContext;
import java.util.function.Supplier;
/**
* @author 六如
*/
public class Configs {
/**
* 获取配置参数
*
* @param keyGetter 配置key
* @return 返回配参数没有则返回null
*/
public static String getValue(ConfigKeyEnum keyGetter) {
return getValue(keyGetter, keyGetter.getDefaultValue());
}
/**
* 获取配置参数
*
* @param keyGetter 配置key
* @param defaultValue 默认值
* @return 返回配参数,没有则返回默认值
*/
public static String getValue(ConfigKeyEnum keyGetter, String defaultValue) {
return SpringContext.getBean(IConfig.class).getConfig(keyGetter.getKey(), defaultValue);
}
/**
* 获取配置参数
*
* @param keyGetter 配置key
* @param defaultValue 默认值
* @return 返回配参数,没有则返回默认值
*/
public static String getValue(ConfigKeyEnum keyGetter, Supplier<String> defaultValue) {
return getValue(keyGetter, defaultValue.get());
}
}

View File

@@ -0,0 +1,18 @@
package com.gitee.sop.adminbackend.config;
import com.gitee.sop.adminbackend.common.SpringContext;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
/**
* @author 六如
*/
@Configuration
public class SopAdminConfiguration implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContext.setApplicationContext(applicationContext);
}
}

View File

@@ -0,0 +1,52 @@
package com.gitee.sop.adminbackend.controller;
import com.gitee.sop.adminbackend.common.Result;
import com.gitee.sop.adminbackend.common.exception.BizException;
import com.gitee.sop.adminbackend.common.exception.ErrorCode;
import com.gitee.sop.adminbackend.common.exception.ExceptionCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
/**
* 全局错误处理
*
* @author tanghc
*/
@RestControllerAdvice
@Slf4j
public class ExceptionHandlerController {
@ExceptionHandler(Exception.class)
public Object exceptionHandler(HttpServletRequest request, Exception e) {
if (e instanceof ExceptionCode) {
ExceptionCode exceptionCode = (ExceptionCode) e;
ErrorCode errorCode = exceptionCode.getCode();
log.error("报错code:{}, msg:{}", errorCode.getCode(), errorCode.getMsg(), e);
return Result.err(errorCode.getCode(), errorCode.getMsg());
}
if (e instanceof BizException || e instanceof IllegalArgumentException) {
RuntimeException bizException = (RuntimeException) e;
return Result.err(bizException.getMessage());
}
// 处理JSR-303错误
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
List<ObjectError> allErrors = exception.getBindingResult().getAllErrors();
String msg = allErrors.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.err(msg);
}
log.error("未知错误URI{}HttpMethod{}", request.getRequestURI(), request.getMethod(), e);
return Result.err("系统错误,请查看日志");
}
}

View File

@@ -0,0 +1,47 @@
package com.gitee.sop.adminbackend.controller.sys;
import com.gitee.sop.adminbackend.common.Result;
import com.gitee.sop.adminbackend.controller.sys.req.LoginParam;
import com.gitee.sop.adminbackend.controller.sys.resp.LoginResultVO;
import com.gitee.sop.adminbackend.service.sys.login.LoginService;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginDTO;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
import com.gitee.sop.adminbackend.util.CopyUtil;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 登录相关接口
*
* @author 六如
*/
@RestController
@RequestMapping("sys")
public class LoginController {
@Resource
private LoginService loginService;
/**
* 用户登录
*
* @param param
* @return
*/
@PostMapping("login")
public Result<LoginResultVO> login(@Validated @RequestBody LoginParam param) {
LoginDTO loginDTO = new LoginDTO();
loginDTO.setUsername(param.getUsername());
loginDTO.setPassword(param.getPassword());
loginDTO.setRegType(RegTypeEnum.BACKEND);
LoginUser loginUser = loginService.login(loginDTO);
LoginResultVO loginResult = CopyUtil.copyBean(loginUser, LoginResultVO::new);
return Result.ok(loginResult);
}
}

View File

@@ -0,0 +1,76 @@
package com.gitee.sop.adminbackend.controller.sys;
import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.param.PageParam;
import com.gitee.sop.adminbackend.common.Result;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 六如
*/
@RestController
@RequestMapping("sys/adminuser")
public class SysAdminUserController {
@Autowired
private SysAdminUserService sysAdminUserService;
/**
* 分页查询
*
* @param param
* @return
*/
@GetMapping("/page")
public Result<PageInfo<SysAdminUser>> page(PageParam param) {
Query query = param.toQuery();
PageInfo<SysAdminUser> pageInfo = sysAdminUserService.page(query);
return Result.ok(pageInfo);
}
/**
* 新增记录
*
* @param user
* @return
*/
@PostMapping("/save")
public Result<Long> save(SysAdminUser user) {
sysAdminUserService.save(user);
// 返回添加后的主键值
return Result.ok(user.getId());
}
/**
* 修改记录
*
* @param user 表单数据
* @return
*/
@PutMapping("/update")
public Result<Integer> update(SysAdminUser user) {
return Result.ok(sysAdminUserService.update(user));
}
/**
* 删除记录
*
* @param id 主键id
* @return
*/
@DeleteMapping("/delete")
public Result<?> delete(Long id) {
return Result.ok(sysAdminUserService.deleteById(id));
}
}

View File

@@ -0,0 +1,14 @@
package com.gitee.sop.adminbackend.controller.sys.req;
import lombok.Data;
import javax.validation.constraints.NotBlank;
@Data
public class LoginParam {
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
}

View File

@@ -0,0 +1,53 @@
package com.gitee.sop.adminbackend.controller.sys.resp;
import lombok.Data;
import java.util.Set;
/**
* @author 六如
*/
@Data
public class LoginResultVO {
private Long id;
/**
* 用户名
*/
private String username;
/**
* 用户名
*/
private String nickname;
/**
* 密码
*/
private String password;
/**
* 头像
*/
private String avatar;
/**
* 注册类型
*/
private Integer regType;
/**
* 状态1启用0禁用
*/
private Integer status;
private Set<String> roles;
private Set<String> permissions;
private String accessToken;
private String refreshToken;
private String expires;
}

View File

@@ -0,0 +1,73 @@
package com.gitee.sop.adminbackend.dao.entity;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 表名api_info
* 备注:接口信息表
*
* @author 六如
*/
@Table(name = "api_info", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class ApiInfo {
private Long id;
/**
* 应用名称
*/
private String application;
/**
* 接口名称
*/
private String apiName;
/**
* 版本号
*/
private String apiVersion;
/**
* 接口class
*/
private String interfaceClassName;
/**
* 方法名称
*/
private String methodName;
/**
* 参数信息
*/
private String paramInfo;
/**
* 接口是否需要授权访问
*/
private Integer isPermission;
/**
* 是否需要appAuthToken
*/
private Integer isNeedToken;
/**
* 状态,1-启用,0-禁用
*/
private Integer status;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,43 @@
package com.gitee.sop.adminbackend.dao.entity;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 表名isv_info
* 备注isv信息表
*
* @author 六如
*/
@Table(name = "isv_info", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class IsvInfo {
private Long id;
/**
* appKey
*/
private String appId;
/**
* 1启用2禁用
*/
private Integer status;
/**
* 备注
*/
private String remark;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,55 @@
package com.gitee.sop.adminbackend.dao.entity;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 表名isv_keys
* 备注ISV秘钥管理
*
* @author 六如
*/
@Table(name = "isv_keys", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class IsvKeys {
private Long id;
private String appId;
/**
* 秘钥格式1PKCS8(JAVA适用)2PKCS1(非JAVA适用)
*/
private Integer keyFormat;
/**
* 开发者生成的公钥
*/
private String publicKeyIsv;
/**
* 开发者生成的私钥(交给开发者)
*/
private String privateKeyIsv;
/**
* 平台生成的公钥(交给开发者)
*/
private String publicKeyPlatform;
/**
* 平台生成的私钥
*/
private String privateKeyPlatform;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,64 @@
package com.gitee.sop.adminbackend.dao.entity;
import java.time.LocalDateTime;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
/**
* 表名sys_admin_user
* 备注:系统用户表
*
* @author 六如
*/
@Table(name = "sys_admin_user", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class SysAdminUser {
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 用户名
*/
private String nickname;
/**
* 邮箱
*/
private String email;
/**
* 头像
*/
private String avatar;
/**
* 状态1启用0禁用
*/
private Integer status;
/**
* 注册类型
*/
private String regType;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,38 @@
package com.gitee.sop.adminbackend.dao.entity;
import java.time.LocalDateTime;
import com.gitee.fastmybatis.annotation.Pk;
import com.gitee.fastmybatis.annotation.PkStrategy;
import com.gitee.fastmybatis.annotation.Table;
import lombok.Data;
/**
* 表名sys_config
* 备注:系统配置表
*
* @author 六如
*/
@Table(name = "sys_config", pk = @Pk(name = "id", strategy = PkStrategy.INCREMENT))
@Data
public class SysConfig {
private Long id;
private String configKey;
private String configValue;
private String remark;
@com.gitee.fastmybatis.annotation.Column(logicDelete = true)
private Integer isDeleted;
private LocalDateTime addTime;
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,19 @@
package com.gitee.sop.adminbackend.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.adminbackend.dao.entity.ApiInfo;
/**
* @author 六如
*/
public interface ApiInfoMapper extends BaseMapper<ApiInfo> {
default ApiInfo getByNameVersion(String apiName, String apiVersion) {
return this.query()
.eq(ApiInfo::getApiName, apiName)
.eq(ApiInfo::getApiVersion, apiVersion)
.get();
}
}

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.adminbackend.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.adminbackend.dao.entity.IsvInfo;
/**
* @author 六如
*/
public interface IsvInfoMapper extends BaseMapper<IsvInfo> {
default IsvInfo getByAppId(String appId) {
return this.get(IsvInfo::getAppId, appId);
}
}

View File

@@ -0,0 +1,15 @@
package com.gitee.sop.adminbackend.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.adminbackend.dao.entity.IsvKeys;
/**
* @author 六如
*/
public interface IsvKeysMapper extends BaseMapper<IsvKeys> {
default IsvKeys getByAppId(String appId) {
return this.get(IsvKeys::getAppId, appId);
}
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.adminbackend.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
/**
* @author 六如
*/
public interface SysAdminUserMapper extends BaseMapper<SysAdminUser> {
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.adminbackend.dao.mapper;
import com.gitee.fastmybatis.core.mapper.BaseMapper;
import com.gitee.sop.adminbackend.dao.entity.SysConfig;
/**
* @author 六如
*/
public interface SysConfigMapper extends BaseMapper<SysConfig> {
}

View File

@@ -0,0 +1,27 @@
package com.gitee.sop.adminbackend.dao.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* @author tanghc
*/
@Mapper
public interface UpgradeMapper {
void runSql(@Param("sql") String sql);
/**
* 查看MYSQL表字段信息
* @param tableName 表名
* @return 返回字段信息
*/
List<Map<String, Object>> listColumnInfo(@Param("tableName") String tableName);
List<String> listTableName();
List<Map<String, Object>> listTableIndex(@Param("tableName") String tableName);
}

View File

@@ -0,0 +1,40 @@
package com.gitee.sop.adminbackend.service.sys;
import com.gitee.fastmybatis.core.support.LambdaService;
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
import com.gitee.sop.adminbackend.config.AdminConfig;
import com.gitee.sop.adminbackend.config.Configs;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
import com.gitee.sop.adminbackend.dao.mapper.SysAdminUserMapper;
import com.gitee.sop.adminbackend.util.GenerateUtil;
import org.springframework.stereotype.Service;
/**
* @author 六如
*/
@Service
public class SysAdminUserService implements LambdaService<SysAdminUser, SysAdminUserMapper> {
public SysAdminUser getByUsername(String username) {
return this.get(SysAdminUser::getUsername, username);
}
public String getDbPassword(String username, String password) {
return getDbPassword(username, password, getPasswordSalt());
}
public String getDbPassword(String username) {
return this.query().eq(SysAdminUser::getUsername, username).getValue(SysAdminUser::getPassword);
}
public String getDbPassword(String username, String password, String salt) {
return GenerateUtil.getUserPassword(username, password, salt);
}
private String getPasswordSalt() {
return Configs.getValue(ConfigKeyEnum.PASSWORD_SALT);
}
}

View File

@@ -0,0 +1,102 @@
package com.gitee.sop.adminbackend.service.sys;
import com.gitee.fastmybatis.core.support.BaseLambdaService;
import com.gitee.sop.adminbackend.common.IConfig;
import com.gitee.sop.adminbackend.dao.entity.SysConfig;
import com.gitee.sop.adminbackend.dao.mapper.SysConfigMapper;
import com.gitee.sop.adminbackend.service.sys.dto.SystemConfigDTO;
import com.gitee.sop.adminbackend.util.CopyUtil;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* @author 六如
*/
@Service
public class SysConfigService extends BaseLambdaService<SysConfig, SysConfigMapper> implements IConfig {
@Resource
private Environment environment;
// key: configKey, value: configValue
private final LoadingCache<String, Optional<String>> configCache = CacheBuilder.newBuilder()
.expireAfterAccess(15, TimeUnit.MINUTES)
.build(new CacheLoader<String, Optional<String>>() {
@Override
public Optional<String> load(String key) throws Exception {
return Optional.ofNullable(getConfigValue(key, null));
}
});
public String getRawValue(String key) {
return this.query()
.eq(SysConfig::getConfigKey, key)
.getValue(SysConfig::getConfigValue);
}
public void setConfig(String key, String value) {
setConfig(key, value, "");
}
public void setConfig(String key, String value, String remark) {
SystemConfigDTO systemConfigDTO = new SystemConfigDTO();
systemConfigDTO.setConfigKey(key);
systemConfigDTO.setConfigValue(value);
systemConfigDTO.setRemark(remark);
setConfig(systemConfigDTO);
}
public void setConfig(SystemConfigDTO systemConfigDTO) {
Objects.requireNonNull(systemConfigDTO.getConfigKey(), "need key");
Objects.requireNonNull(systemConfigDTO.getConfigValue(), "need value");
SysConfig systemConfig = get(SysConfig::getConfigKey, systemConfigDTO.getConfigKey());
if (systemConfig == null) {
systemConfig = CopyUtil.copyBean(systemConfigDTO, SysConfig::new);
this.save(systemConfig);
} else {
CopyUtil.copyPropertiesIgnoreNull(systemConfigDTO, systemConfig);
this.update(systemConfig);
}
configCache.invalidate(systemConfigDTO.getConfigKey());
}
/**
* 获取配置信息
* <pre>
* 优先级:
* 数据库
* Environment
* 默认配置
* </pre>
*
* @param key 配置key
* @param defaultValue 没有获取到返回的默认值
* @return 返回配置信息,如果没有获取到值,则返回默认值
*/
public String getConfigValue(String key, String defaultValue) {
Objects.requireNonNull(key, "need key");
SysConfig systemConfig = get(SysConfig::getConfigKey, key);
return Optional.ofNullable(systemConfig)
.map(SysConfig::getConfigValue)
.orElseGet(() -> environment.getProperty(key, defaultValue));
}
@Override
public String getConfig(String key) {
return configCache.getUnchecked(key).orElse(null);
}
@Override
public String getConfig(String key, String defaultValue) {
return configCache.getUnchecked(key).orElse(defaultValue);
}
}

View File

@@ -0,0 +1,67 @@
package com.gitee.sop.adminbackend.service.sys;
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
import com.gitee.sop.adminbackend.dao.mapper.UpgradeMapper;
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
import com.gitee.sop.adminbackend.util.PasswordUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* @author 六如
*/
@Service
public class UpgradeService {
@Resource
private SysConfigService sysConfigService;
@Resource
private SysAdminUserService sysAdminUserService;
@Resource
private UpgradeMapper upgradeMapper;
@PostConstruct
public void init() {
this.initJwtSecret();
this.insertAdmin();
}
private void initJwtSecret() {
String configKey = ConfigKeyEnum.JWT_SECRET.getKey();
String value = sysConfigService.getRawValue(configKey);
if (StringUtils.isBlank(value)) {
value = PasswordUtil.getRandomSimplePassword(30);
sysConfigService.setConfig(configKey, value);
}
}
public void insertAdmin() {
SysAdminUser userInfo = sysAdminUserService.getByUsername("admin");
if (userInfo != null) {
return;
}
String username = "admin";
String tpl = "INSERT INTO `sys_admin_user` ( `username`, `password`, `nickname`, `reg_type`) VALUES \n" +
"\t('%s','%s','%s','%s');";
// 初始密码
String defPassword = "123456";
defPassword = DigestUtils.sha256Hex(defPassword);
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
String sql = String.format(tpl, username, encodedPassword, username, RegTypeEnum.BACKEND.getValue());
runSql(sql);
}
protected void runSql(String sql) {
upgradeMapper.runSql(sql);
}
}

View File

@@ -0,0 +1,18 @@
package com.gitee.sop.adminbackend.service.sys.dto;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class SystemConfigDTO {
/** 数据库字段config_key */
private String configKey;
/** 数据库字段config_value */
private String configValue;
/** 数据库字段remark */
private String remark;
}

View File

@@ -0,0 +1,153 @@
package com.gitee.sop.adminbackend.service.sys.login;
import com.alibaba.nacos.shaded.com.google.common.collect.Sets;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminbackend.common.ConfigKeyEnum;
import com.gitee.sop.adminbackend.common.exception.BizException;
import com.gitee.sop.adminbackend.config.Configs;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginDTO;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginForm;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginResult;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
import com.gitee.sop.adminbackend.service.sys.login.enums.AdminUserStatusEnum;
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
import com.gitee.sop.adminbackend.util.CopyUtil;
import com.gitee.sop.adminbackend.util.GenerateUtil;
import com.gitee.sop.adminbackend.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author 六如
*/
@Service
@Slf4j
public class LoginService {
@Resource
private SysAdminUserService sysAdminUserService;
@Resource
private UserCacheManager userCacheManager;
public LoginUser login(LoginDTO loginDTO) {
String username = loginDTO.getUsername();
String password = loginDTO.getPassword();
RegTypeEnum regType = loginDTO.getRegType();
SysAdminUser userInfo;
switch (regType) {
case FORM:
throw new UnsupportedOperationException("第三方登录暂未支持");
case LDAP:
// LDAP登录
throw new UnsupportedOperationException("LDAP登录登录暂未支持");
default: {
// 默认注册账号登录
userInfo = this.doDatabaseLogin(username, password);
}
}
LoginUser loginUser = buildLoginUser(userInfo);
// 保存到缓存
userCacheManager.saveUser(loginUser);
return loginUser;
}
private LoginUser buildLoginUser(SysAdminUser userInfo) {
if (AdminUserStatusEnum.of(userInfo.getStatus()) == AdminUserStatusEnum.DISABLED) {
throw new BizException("账号已禁用,请联系管理员");
}
// 登录成功
LoginUser loginUser = CopyUtil.copyBean(userInfo, LoginUser::new);
// 创建token
String token = this.createToken(userInfo.getId());
loginUser.setAccessToken(token);
if ("admin".equals(userInfo.getUsername())) {
// ROLE
loginUser.setRoles(Sets.newHashSet("admin"));
// *:*:* 表示所有权限
loginUser.setPermissions(Sets.newHashSet("*:*:*"));
} else {
// TODO:其它角色权限
loginUser.setRoles(Sets.newHashSet());
loginUser.setPermissions(Sets.newHashSet());
}
// 设置token过期时间
String value = Configs.getValue(ConfigKeyEnum.JWT_TIMEOUT_DAYS);
LocalDateTime expireDate = LocalDateTime.now().plusDays(NumberUtils.toInt(value));
loginUser.setExpires(expireDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));
return loginUser;
}
private String createToken(long userId) {
Map<String, String> data = new HashMap<>(4);
data.put("id", String.valueOf(userId));
String value = Configs.getValue(ConfigKeyEnum.JWT_TIMEOUT_DAYS);
return JwtUtil.createJwt(data, NumberUtils.toInt(value), getJwtSecret());
}
public static String getJwtSecret() {
return Configs.getValue(ConfigKeyEnum.JWT_SECRET);
}
private SysAdminUser doThirdPartyLogin(ThirdPartyLoginManager thirdPartyLoginManager, String username, String password) {
LoginForm loginForm = new LoginForm();
loginForm.setUsername(username);
loginForm.setPassword(password);
LoginResult loginResult;
try {
loginResult = thirdPartyLoginManager.login(loginForm);
} catch (Exception e) {
log.error("第三方登录失败", e);
throw new BizException(e.getMessage());
}
SysAdminUser userInfo = sysAdminUserService.getByUsername(username);
// 用户第一次登录则插入到user_info表
if (userInfo == null) {
userInfo = new SysAdminUser();
userInfo.setUsername(username);
userInfo.setPassword(GenerateUtil.getUUID());
userInfo.setNickname(loginResult.getNickname());
userInfo.setAvatar("");
userInfo.setStatus(AdminUserStatusEnum.ENABLE.getStatus());
userInfo.setRegType(loginResult.getRegTypeEnum().getValue());
userInfo.setEmail(loginResult.getEmail());
sysAdminUserService.save(userInfo);
} else {
String email = loginResult.getEmail();
// 如果更改了邮箱
if (StringUtils.hasText(email) && !Objects.equals(email, userInfo.getEmail())) {
userInfo.setEmail(email);
sysAdminUserService.update(userInfo);
}
}
return userInfo;
}
private SysAdminUser doDatabaseLogin(String username, String password) {
SysAdminUser sysAdminUser = sysAdminUserService.getByUsername(username);
Assert.notNull(sysAdminUser, () -> "用户名密码不正确");
String encodedPasswordDb = sysAdminUser.getPassword();
// 校验
boolean flag = BCrypt.checkpw(password, encodedPasswordDb);
Assert.isTrue(flag, () -> "用户名密码不正确");
return sysAdminUser;
}
}

View File

@@ -0,0 +1,40 @@
package com.gitee.sop.adminbackend.service.sys.login;
/**
* @author thc
*/
public class NotNullStringBuilder {
private final StringBuilder stringBuilder = new StringBuilder();
public NotNullStringBuilder append(Number o) {
stringBuilder.append(formatValue(o));
return this;
}
public NotNullStringBuilder append(String o) {
stringBuilder.append(formatValue(o));
return this;
}
public NotNullStringBuilder append(String o, String defaultValue) {
if (o == null || o.length() == 0) {
o = defaultValue;
}
stringBuilder.append(formatValue(o));
return this;
}
private static String formatValue(Number o) {
return o == null ? "0" : String.valueOf(o);
}
private static String formatValue(String o) {
return o == null ? "" : o;
}
@Override
public String toString() {
return stringBuilder.toString();
}
}

View File

@@ -0,0 +1,17 @@
package com.gitee.sop.adminbackend.service.sys.login;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginForm;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginResult;
/**
* @author 六如
*/
public interface ThirdPartyLoginManager {
/**
* 第三方登录
* @param loginForm 登录表单
* @return 返回登录结果
*/
LoginResult login(LoginForm loginForm) throws Exception;
}

View File

@@ -0,0 +1,21 @@
package com.gitee.sop.adminbackend.service.sys.login;
import com.gitee.sop.adminbackend.common.User;
/**
* @author 六如
*/
public interface UserCacheManager {
/**
* 返回用户信息
* @param userId 用户id
* @return 查不到返回null
*/
User getUser(long userId);
/**
* 保存用户
* @param user 用户
*/
void saveUser(User user);
}

View File

@@ -0,0 +1,11 @@
package com.gitee.sop.adminbackend.service.sys.login.dto;
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
import lombok.Data;
@Data
public class LoginDTO {
private String username;
private String password;
private RegTypeEnum regType = RegTypeEnum.BACKEND;
}

View File

@@ -0,0 +1,12 @@
package com.gitee.sop.adminbackend.service.sys.login.dto;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class LoginForm {
private String username;
private String password;
}

View File

@@ -0,0 +1,20 @@
package com.gitee.sop.adminbackend.service.sys.login.dto;
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
import lombok.Data;
/**
* @author 六如
*/
@Data
public class LoginResult {
private String username;
private String nickname;
private String email;
private RegTypeEnum regTypeEnum = RegTypeEnum.FORM;
}

View File

@@ -0,0 +1,83 @@
package com.gitee.sop.adminbackend.service.sys.login.dto;
import com.gitee.sop.adminbackend.common.User;
import lombok.Data;
import java.util.Set;
/**
* @author 六如
*/
@Data
public class LoginUser implements User {
/*
avatar: "https://avatars.githubusercontent.com/u/44761321",
username: "admin",
nickname: "小铭",
// 一个用户可能有多个角色
roles: ["admin"],
// 按钮级别权限
permissions: ["*:*:*"],
accessToken: "eyJhbGciOiJIUzUxMiJ9.admin",
refreshToken: "eyJhbGciOiJIUzUxMiJ9.adminRefresh",
expires: "2030/10/30 00:00:00"
*/
private Long id;
/**
* 用户名
*/
private String username;
/**
* 用户名
*/
private String nickname;
/**
* 密码
*/
private String password;
/**
* 头像
*/
private String avatar;
/**
* 注册类型
*/
private Integer regType;
/**
* 状态1启用0禁用
*/
private Integer status;
private Set<String> roles;
private Set<String> permissions;
private String accessToken;
private String refreshToken;
private String expires;
@Override
public Long getUserId() {
return id;
}
@Override
public Integer getStatus() {
return status;
}
@Override
public String getToken() {
return accessToken;
}
}

View File

@@ -0,0 +1,34 @@
package com.gitee.sop.adminbackend.service.sys.login.enums;
import java.util.Objects;
/**
* @author 六如
*/
public enum AdminUserStatusEnum {
DISABLED((byte)0),
ENABLE((byte)1),
SET_PWD((byte)2),
;
private final int status;
public static AdminUserStatusEnum of(Integer value) {
for (AdminUserStatusEnum adminUserStatusEnum : AdminUserStatusEnum.values()) {
if (Objects.equals(adminUserStatusEnum.status, value)) {
return adminUserStatusEnum;
}
}
return DISABLED;
}
AdminUserStatusEnum(byte style) {
this.status = style;
}
public int getStatus() {
return status;
}
}

View File

@@ -0,0 +1,30 @@
package com.gitee.sop.adminbackend.service.sys.login.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author 六如
*/
@AllArgsConstructor
@Getter
public enum RegTypeEnum {
BACKEND("backend"),
FORM("form"),
OAUTH("oauth"),
LDAP("ldap"),
;
public static RegTypeEnum of(String source) {
for (RegTypeEnum value : RegTypeEnum.values()) {
if (Objects.equals(source, value.value)) {
return value;
}
}
return RegTypeEnum.BACKEND;
}
private final String value;
}

View File

@@ -0,0 +1,90 @@
package com.gitee.sop.adminbackend.service.sys.login.impl;
import com.gitee.sop.adminbackend.common.User;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
import com.gitee.sop.adminbackend.service.sys.login.UserCacheManager;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
import com.gitee.sop.adminbackend.service.sys.login.enums.AdminUserStatusEnum;
import com.gitee.sop.adminbackend.util.CopyUtil;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* @author 六如
*/
@Service
@Slf4j
public class DefaultUserCacheManager implements UserCacheManager, InitializingBean {
@Autowired
private SysAdminUserService sysAdminUserService;
@Value("${admin.user-cache-timeout-minutes:15}")
private int timeoutMinutes;
// key: userId
private LoadingCache<Long, Optional<User>> userCache;
@Override
public User getUser(long userId) {
return userCache.getUnchecked(userId).orElse(null);
}
@Override
public void saveUser(User user) {
if (user == null) {
return;
}
userCache.put(user.getUserId(), Optional.of(user));
}
private LoadingCache<Long, Optional<User>> buildCache(int timeout) {
if (timeout <= 0) {
throw new IllegalArgumentException("timeout must be gt 0");
}
CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
cacheBuilder.expireAfterAccess(timeout, TimeUnit.MINUTES);
return cacheBuilder
.build(new CacheLoader<Long, Optional<User>>() {
@Override
public Optional<User> load(Long id) throws Exception {
User user = getLoginUser(id);
return Optional.ofNullable(user);
}
});
}
/**
* 获取登陆用户
* @param id
* @return
*/
private User getLoginUser(long id) {
SysAdminUser userInfo = sysAdminUserService.getById(id);
if (userInfo == null) {
log.warn("登录用户不存在userId{}", id);
return null;
}
if (userInfo.getStatus() == AdminUserStatusEnum.DISABLED.getStatus()) {
log.warn("用户被禁用, userId:{}, username:{}, nickname:{}", userInfo.getId(), userInfo.getUsername(), userInfo.getNickname());
return null;
}
return CopyUtil.copyBean(userInfo, LoginUser::new);
}
@Override
public void afterPropertiesSet() throws Exception {
userCache = buildCache(timeoutMinutes);
}
}

View File

@@ -0,0 +1,344 @@
package com.gitee.sop.adminbackend.util;
import com.alibaba.fastjson2.JSON;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* 属性拷贝工具类
*
* @author 六如
*/
public class CopyUtil extends BeanUtils {
/**
* 属性拷贝,第一个参数中的属性值拷贝到第二个参数中<br>
* 注意:当第一个参数中的属性有null值时,不会拷贝进去
*
* @param from 源对象
* @param to 目标对象
* @param ignoreProperties 忽略的字段
* @throws BeansException
*/
public static void copyPropertiesIgnoreNull(Object from, Object to, String... ignoreProperties)
throws BeansException {
Assert.notNull(from, "Source must not be null");
Assert.notNull(to, "Target must not be null");
Class<?> actualEditable = to.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : Collections.emptyList());
for (PropertyDescriptor targetPd : targetPds) {
if (ignoreList.contains(targetPd.getName())) {
continue;
}
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null) {
PropertyDescriptor sourcePd = getPropertyDescriptor(from.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(from);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
// 这里判断value是否为空 当然这里也能进行一些特殊要求的处理
// 例如绑定时格式转换等等
if (value != null) {
writeMethod.invoke(to, value);
}
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
/**
* 拷贝指定的字段
*
* @param from 源对象
* @param to 目标对象
* @param includeFields 指定字段
*/
public static void copyPropertiesInclude(Object from, Object to, Set<String> includeFields) {
Objects.requireNonNull(includeFields, "includeFields can not null");
Assert.notNull(from, "Source must not be null");
Assert.notNull(to, "Target must not be null");
if (includeFields.isEmpty()) {
return;
}
Class<?> actualEditable = to.getClass();
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
for (PropertyDescriptor targetPd : targetPds) {
if (!includeFields.contains(targetPd.getName())) {
continue;
}
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null) {
PropertyDescriptor sourcePd = getPropertyDescriptor(from.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(from);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(to, value);
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
/**
* 拷贝属性
*
* @param from 被拷贝类
* @param to 目标类
*/
public static void copyProperties(Object from, Object to) {
BeanUtils.copyProperties(from, to);
}
/**
* 拷贝bean成为一个新类
*
* @param from 被拷贝类
* @param supplier 新的类获取回调
* @param <T> 新的类
* @return 返回新的类实例from为null时返回null
*/
public static <T> T copyBean(Object from, Supplier<T> supplier) {
if (from == null) {
return null;
}
T to = supplier.get();
BeanUtils.copyProperties(from, to);
return to;
}
/**
* 拷贝实例
*
* @param from 被拷贝类
* @param supplier 新的类获取回调
* @param after 对新的类最后续处理回调
* @param <T> 新的类
* @return 返回新的类
*/
public static <T> T copyBean(Object from, Supplier<T> supplier, Consumer<T> after) {
if (from == null) {
return null;
}
T to = supplier.get();
BeanUtils.copyProperties(from, to);
after.accept(to);
return to;
}
/**
* 拷贝List将list中的类转换成新的对象
*
* @param collection 被拷贝的集合
* @param toElement List新元素
* @param <T> 新元素类型
* @return 返回新的List
*/
public static <T> List<T> copyList(Collection<?> collection, Supplier<T> toElement) {
if (collection == null || collection.isEmpty()) {
return new ArrayList<>();
}
return collection.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
return target;
})
.collect(Collectors.toList());
}
public static <E, R> List<R> copyList(Collection<E> fromList, Function<E, R> function) {
if (fromList == null) {
return new ArrayList<>();
}
return fromList.stream()
.map(source -> {
R target = function.apply(source);
BeanUtils.copyProperties(source, target);
return target;
})
.collect(Collectors.toList());
}
/**
* 拷贝List并做后续处理
*
* @param fromList 被拷贝的list
* @param toElement 新元素
* @param after 对新元素做后续处理
* @param <T> 新类型
* @return 返回新的List
*/
public static <T> List<T> copyList(Collection<?> fromList, Supplier<T> toElement, Consumer<T> after) {
if (fromList == null) {
return new ArrayList<>();
}
return fromList.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
after.accept(target);
return target;
})
.collect(Collectors.toList());
}
/**
* 拷贝List并做后续处理
*
* @param fromList 被拷贝的list
* @param toElement 新元素
* @param after 对新元素做后续处理
* @param <T> 新类型
* @return 返回新的List
*/
public static <T, F> List<T> copyList(Collection<F> fromList, Supplier<T> toElement, CopyConsumer<F, T> after) {
if (fromList == null) {
return new ArrayList<>();
}
return fromList.stream()
.map(source -> {
T target = toElement.get();
BeanUtils.copyProperties(source, target);
after.apply(source, target);
return target;
})
.collect(Collectors.toList());
}
/**
* 深层次拷贝通过json转换的方式实现
*
* @param from 待转换的类
* @param toClass 目标类class
* @param <T> 目标类
* @return 返回目标类
*/
public static <T> T deepCopy(Object from, Class<T> toClass) {
String json = JSON.toJSONString(from);
return JSON.parseObject(json, toClass);
}
/**
* 深层次拷贝通过json转换的方式实现
*
* @param from 待转换的类
* @param toClass 目标类class
* @param <T> 目标类
* @return 返回目标类
*/
public static <T> List<T> deepCopyList(Object from, Class<T> toClass) {
String json = JSON.toJSONString(from);
return JSON.parseArray(json, toClass);
}
/**
* 拷贝map
*
* @param srcMap 原map
* @param valueGetter 值转换
* @param <K> Key类型
* @param <V> Value类型
* @return 返回新map
*/
public static <K, V> Map<K, V> copyMap(Map<K, ?> srcMap, Supplier<V> valueGetter) {
Map<K, V> ret = new LinkedHashMap<>(srcMap.size() * 2);
for (Map.Entry<K, ?> entry : srcMap.entrySet()) {
V value = copyBean(entry.getValue(), valueGetter);
ret.put(entry.getKey(), value);
}
return ret;
}
/**
* 拷贝map
*
* @param srcMap 原map
* @param function 值转换
* @param <K> Key类型
* @param <V> Value类型
* @return 返回新map
*/
public static <K, V, V0> Map<K, V> copyMap(Map<K, V0> srcMap, Function<V0, V> function) {
Map<K, V> ret = new LinkedHashMap<>(srcMap.size() * 2);
for (Map.Entry<K, V0> entry : srcMap.entrySet()) {
V value = function.apply(entry.getValue());
ret.put(entry.getKey(), value);
}
return ret;
}
/**
* 拷贝map,value是list
*
* @param srcMap 原map
* @param valueGetter 值转换
* @param <K> Key类型
* @param <V> Value类型
* @return 返回新map
*/
public static <K, V, V2> Map<K, List<V2>> copyMapList(Map<K, List<V>> srcMap, Function<List<V>, List<V2>> valueGetter) {
Map<K, List<V2>> ret = new LinkedHashMap<>(srcMap.size() * 2);
for (Map.Entry<K, List<V>> entry : srcMap.entrySet()) {
List<V2> value = valueGetter.apply(entry.getValue());
ret.put(entry.getKey(), value);
}
return ret;
}
public interface CopyConsumer<F, T> {
void apply(F from, T to);
}
}

View File

@@ -0,0 +1,22 @@
package com.gitee.sop.adminbackend.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
/**
* @author 六如
*/
@Slf4j
public class GenerateUtil {
public static String getUserPassword(String username, String password, String salt) {
return DigestUtils.md5DigestAsHex((username + password + salt).getBytes(StandardCharsets.UTF_8));
}
public static String getUUID() {
return IdGen.uuid();
}
}

View File

@@ -0,0 +1,171 @@
package com.gitee.sop.adminbackend.util;
import java.util.UUID;
public class IdGen {
private static long workId = 0;
private static final SnowflakeIdWorker worker = new SnowflakeIdWorker(workId++, 0);
public static long genId() {
return worker.nextId();
}
/**
* 生成唯一id
* @return
*/
public static String nextId() {
return String.valueOf(genId());
}
public static String uuid() {
return UUID.randomUUID().toString().replace("-", "");
}
/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0<br>
* 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)
* 得到的值这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性。41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId<br>
* 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位为一个Long型。<br>
* SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)并且效率较高经测试SnowFlake每秒能够产生26万ID左右。
*/
public static class SnowflakeIdWorker {
// ==============================Fields===========================================
/** 开始时间截 (2015-01-01) */
private final long twepoch = 1420041100000L;
/** 机器id所占的位数 */
private final long workerIdBits = 5L;
/** 数据标识id所占的位数 */
private final long datacenterIdBits = 5L;
/** 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/** 支持的最大数据标识id结果是31 */
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/** 序列在id中占的位数 */
private final long sequenceBits = 12L;
/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 数据标识id向左移17位(12+5) */
private final long datacenterIdShift = sequenceBits + workerIdBits;
/** 时间截向左移22位(5+5+12) */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/** 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095) */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 工作机器ID(0~31) */
private long workerId;
/** 数据中心ID(0~31) */
private long datacenterId;
/** 毫秒内序列(0~4095) */
private long sequence = 0L;
/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;
//==============================Constructors=====================================
/**
* 构造函数
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
// ==============================Methods==========================================
/**
* 获得下一个ID (该方法是线程安全的)
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
* @return 当前时间(毫秒)
*/
protected long timeGen() {
return System.currentTimeMillis();
}
}
/*//==============================Test=============================================
*/
/** 测试
public static void main(String[] args) {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 100; i++) {
long id = idWorker.nextId();
System.out.println("id:" + id);
System.out.println("toBinaryString:" + Long.toBinaryString(id));
}
}
*/
}

View File

@@ -0,0 +1,97 @@
package com.gitee.sop.adminbackend.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.gitee.sop.adminbackend.common.exception.JwtCreateException;
import com.gitee.sop.adminbackend.common.exception.JwtErrorException;
import com.gitee.sop.adminbackend.common.exception.JwtExpiredException;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author tanghc
*/
@Slf4j
public class JwtUtil {
private static final Map<String, Object> headerClaims = new HashMap<>();
/**
* 登录成功后跳转页面
*/
private static final String SUCCESS_HTML = String.join("\n", Arrays.asList(
"<html><head><script>",
"localStorage.setItem('torna.token', '%s');",
"location.href = '%s';",
"</script></head><body></body></html>"
));
static {
headerClaims.put("typ", "JWT");
headerClaims.put("alg", "HS256");
}
public static String createJwt(Map<String, String> data, int timeoutDays, String secret) {
JWTCreator.Builder builder = JWT.create().withHeader(headerClaims);
Set<Map.Entry<String, String>> entrySet = data.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
builder.withClaim(entry.getKey(), entry.getValue());
}
LocalDateTime expiredDay = LocalDateTime.now().plusDays(timeoutDays);
Date expiredDate = Date.from(expiredDay.atZone(ZoneId.systemDefault()).toInstant());
try {
return builder
// 过期时间
.withExpiresAt(expiredDate)
// 创建时间
.withIssuedAt(new Date())
// 签名
.sign(Algorithm.HMAC256(secret));
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new JwtCreateException();
}
}
public static Map<String, Claim> verifyJwt(String token, String secret) throws JwtExpiredException, JwtErrorException {
JWTVerifier verifier = null;
try {
verifier = JWT.require(Algorithm.HMAC256(secret)).build();
} catch (Exception e) {
log.error("验证jwt失败", e);
throw new JwtErrorException();
}
DecodedJWT jwt;
try {
jwt = verifier.verify(token);
} catch (TokenExpiredException e) {
throw new JwtExpiredException();
} catch (Exception e) {
log.error("验证jwt失败", e);
throw new JwtErrorException();
}
return jwt.getClaims();
}
public static String getJumpPageHtml(String token) {
return getJumpPageHtml(token, "/");
}
public static String getJumpPageHtml(String token, String redirectUrl) {
return String.format(SUCCESS_HTML, token, redirectUrl);
}
}

View File

@@ -0,0 +1,64 @@
package com.gitee.sop.adminbackend.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class PasswordUtil {
private static final List<Character> CHARS;
private static final List<Character> SIMPLE_CHARS;
static {
CHARS = getCharList("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890~!@#$%^&*-=,.<>");
SIMPLE_CHARS = getCharList("abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789");
}
private static List<Character> getCharList(String str) {
char[] chars = str.toCharArray();
List<Character> list = new ArrayList<>(chars.length);
for (char c : chars) {
list.add(c);
}
Collections.shuffle(list);
return list;
}
public static void main(String[] args) {
String password = getRandomPassword(32);
System.out.println(password);
}
/**
* 随机密码生成,仅字母数字
* @param len 密码长度必须大于等于4
*/
public static String getRandomSimplePassword(int len) {
if (len < 4) {
throw new IllegalArgumentException("'len' must >= 4");
}
StringBuilder sb = new StringBuilder();
Random r = new Random();
for (int x = 0; x < len; ++x) {
sb.append(SIMPLE_CHARS.get(r.nextInt(SIMPLE_CHARS.size())));
}
return sb.toString();
}
/**
* 随机密码生成
* @param len 密码长度必须大于等于6
*/
public static String getRandomPassword(int len) {
if (len < 6) {
throw new IllegalArgumentException("'len' must >= 6");
}
StringBuilder sb = new StringBuilder();
Random r = new Random();
for (int x = 0; x < len; ++x) {
sb.append(CHARS.get(r.nextInt(CHARS.size())));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,8 @@
dubbo.registry.address=zookeeper://localhost:2181
mybatis.print-sql=true
# mysql config
mysql.host=127.0.0.1:3306
mysql.username=root
mysql.password=root

View File

@@ -0,0 +1,2 @@
dubbo.registry.address=nacos://localhost:8848

View File

@@ -0,0 +1,33 @@
server.port=8082
spring.profiles.active=dev
spring.application.name=sop-admin-backend
dubbo.protocol.name=dubbo
dubbo.protocol.port=-1
dubbo.application.qos-enable=false
dubbo.registry.address=zookeeper://localhost:2181
####### mysql config #######
mysql.host=127.0.0.1:3306
mysql.username=
mysql.password=
mysql.db=sop
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${mysql.host}/${mysql.db}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password}
####### mybatis config #######
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillInsert=add_time
mybatis.fill.com.gitee.fastmybatis.core.support.LocalDateTimeFillUpdate=update_time
# mybatis config file
mybatis.config-location=classpath:mybatis/mybatisConfig.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
# print SQL
logging.level.com.gitee.sop.adminbackend.dao=error
logging.level.com.gitee.fastmybatis=info
mybatis.print-sql=false

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.gitee.sop.adminbackend.dao.mapper.UpgradeMapper">
<update id="runSql">
${sql}
</update>
<select id="listColumnInfo" resultType="java.util.Map">
SHOW COLUMNS FROM ${tableName}
</select>
<select id="listTableName" resultType="String">
SHOW TABLES
</select>
<select id="listTableIndex" resultType="java.util.Map">
show index from ${tableName}
</select>
</mapper>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 全局映射器启用缓存 -->
<setting name="cacheEnabled" value="true" />
<!-- 查询时,关闭关联对象即时加载以提高性能 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 对于未知的SQL查询允许返回不同的结果集以达到通用的效果 -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 允许使用列标签代替列名 -->
<setting name="useColumnLabel" value="true" />
<!-- 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值)数据表的PK生成策略将被覆盖 -->
<setting name="useGeneratedKeys" value="false" />
<!-- 对于批量更新操作缓存SQL以提高性能:BATCH -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 超时设置 -->
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<plugins>
<plugin interceptor="com.gitee.fastmybatis.core.support.plugin.SqlFormatterPlugin">
</plugin>
</plugins>
</configuration>

View File

@@ -0,0 +1,13 @@
package com.gitee.sop.adminbackend;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BaseTest {
@Test
void contextLoads() {
}
}

View File

@@ -0,0 +1,44 @@
package com.gitee.sop.adminbackend.service;
import com.alibaba.fastjson2.JSON;
import com.gitee.sop.adminbackend.BaseTest;
import com.gitee.sop.adminbackend.service.sys.login.LoginService;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginDTO;
import com.gitee.sop.adminbackend.service.sys.login.dto.LoginUser;
import com.gitee.sop.adminbackend.service.sys.login.enums.RegTypeEnum;
import javax.annotation.Resource;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.util.Assert;
/**
* @author 六如
*/
public class LoginServiceTest extends BaseTest {
@Resource
LoginService loginService;
@Test
public void login() {
LoginDTO loginDTO = new LoginDTO();
loginDTO.setUsername("admin");
loginDTO.setPassword("123456");
loginDTO.setRegType(RegTypeEnum.BACKEND);
LoginUser loginUser = loginService.login(loginDTO);
Assert.notNull(loginUser, "not null");
System.out.println(JSON.toJSONString(loginUser));
}
@Test
public void resetAdminPwd() {
// 初始密码
String defPassword = "123456";
defPassword = DigestUtils.sha256Hex(defPassword);
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
}
}

View File

@@ -0,0 +1,37 @@
package com.gitee.sop.adminbackend.service;
import com.gitee.sop.adminbackend.BaseTest;
import com.gitee.sop.adminbackend.dao.entity.SysAdminUser;
import com.gitee.sop.adminbackend.service.sys.SysAdminUserService;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import org.springframework.security.crypto.bcrypt.BCrypt;
import javax.annotation.Resource;
/**
* @author 六如
*/
public class PasswordTest extends BaseTest {
@Resource
SysAdminUserService sysAdminUserService;
/**
* 重置admin密码
*/
@Test
public void resetAdminPwd() {
String username = "admin";
String defPassword = "123456";
defPassword = DigestUtils.sha256Hex(defPassword);
String encodedPassword = BCrypt.hashpw(defPassword, BCrypt.gensalt());
System.out.println("数据库保存:" + encodedPassword);
sysAdminUserService.query()
.eq(SysAdminUser::getUsername, username)
.set(SysAdminUser::getPassword, encodedPassword)
.update();
}
}