sk-matrix-service 项目 api + services
This commit is contained in:
parent
9c8f7fcfc5
commit
f54213d8ef
@ -16,14 +16,15 @@
|
||||
<entry name="D:/Repository/maven/org/mapstruct/mapstruct-processor/1.5.3.Final/mapstruct-processor-1.5.3.Final.jar" />
|
||||
<entry name="D:/Repository/maven/org/mapstruct/mapstruct/1.5.3.Final/mapstruct-1.5.3.Final.jar" />
|
||||
<entry name="D:/Repository/maven/org/projectlombok/lombok/1.18.24/lombok-1.18.24.jar" />
|
||||
<entry name="D:/Repository/maven/org/projectlombok/lombok-mapstruct-binding/0.2.0/lombok-mapstruct-binding-0.2.0.jar" />
|
||||
<entry name="D:/Repository/maven/org/springframework/boot/spring-boot-configuration-processor/3.0.1/spring-boot-configuration-processor-3.0.1.jar" />
|
||||
</processorPath>
|
||||
<module name="annotation" />
|
||||
<module name="common" />
|
||||
<module name="starter" />
|
||||
<module name="common" />
|
||||
<module name="auth" />
|
||||
<module name="orm" />
|
||||
<module name="api" />
|
||||
<module name="orm" />
|
||||
<module name="services" />
|
||||
<module name="casbin" />
|
||||
</profile>
|
||||
|
@ -36,6 +36,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.skcks.matrix.v2</groupId>
|
||||
<artifactId>services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
|
@ -0,0 +1,78 @@
|
||||
package cn.skcks.matrix.v2.api.auth;
|
||||
|
||||
import cn.skcks.matrix.v2.annotation.web.JsonMapping;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.PostJson;
|
||||
import cn.skcks.matrix.v2.config.swagger.SwaggerConfig;
|
||||
import cn.skcks.matrix.v2.model.auth.dto.UserLoginDto;
|
||||
import cn.skcks.matrix.v2.model.auth.dto.UserRegisterDto;
|
||||
import cn.skcks.matrix.v2.model.auth.vo.UserLoginVo;
|
||||
import cn.skcks.matrix.v2.model.auth.vo.UserRegisterVo;
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import cn.skcks.matrix.v2.services.auth.AuthService;
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinRegister;
|
||||
import cn.skcks.matrix.v2.services.role.RoleService;
|
||||
import cn.skcks.matrix.v2.utils.json.JsonResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.casbin.jcasbin.main.Enforcer;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static cn.skcks.matrix.v2.services.casbin.CasbinService.DEFAULT_SYSTEM;
|
||||
|
||||
@Tag(name = "用户认证")
|
||||
@RestController
|
||||
@JsonMapping("/auth")
|
||||
@RequiredArgsConstructor
|
||||
public class AuthController implements CasbinRegister {
|
||||
private final AuthService authService;
|
||||
|
||||
public final static String LOGIN_PERMISSION = "login";
|
||||
public final static String LOGIN_PERMISSION_NAME = "登录";
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi authApi() {
|
||||
return SwaggerConfig.api("Auth", "/auth");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Enforcer enforcer, List<RouteInfo> routes, RoleService roleService) {
|
||||
log.info("[casbin] 初始化 {} 权限", LOGIN_PERMISSION_NAME);
|
||||
roleService.initRole(LOGIN_PERMISSION, LOGIN_PERMISSION_NAME, true);
|
||||
|
||||
List<RouteInfo> filterRoutes = routes.stream()
|
||||
.filter(routeInfo -> Pattern.matches("/auth/(register|login)", routeInfo.requestUrl))
|
||||
.toList();
|
||||
|
||||
CasbinRegister.addPolicies(enforcer, LOGIN_PERMISSION, DEFAULT_SYSTEM, filterRoutes);
|
||||
}
|
||||
|
||||
@Operation(summary = "用户注册")
|
||||
@PostJson("/register")
|
||||
public JsonResponse<UserRegisterVo> registerUser(@RequestBody @Validated UserRegisterDto params) {
|
||||
UserRegisterVo vo = authService.register(params);
|
||||
if (vo.isStatus()) {
|
||||
return JsonResponse.success(vo);
|
||||
} else {
|
||||
return JsonResponse.error(vo);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "用户登录")
|
||||
@PostJson("/login")
|
||||
public JsonResponse<UserLoginVo> registerUser(@RequestBody @Validated UserLoginDto params) {
|
||||
UserLoginVo vo = authService.login(params);
|
||||
if (vo.isStatus()) {
|
||||
return JsonResponse.success(vo);
|
||||
} else {
|
||||
return JsonResponse.error(null, vo.getException());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package cn.skcks.matrix.v2.api.role;
|
||||
|
||||
import cn.skcks.matrix.v2.annotation.web.JsonMapping;
|
||||
import cn.skcks.matrix.v2.annotation.web.auth.Auth;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.DeleteJson;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.GetJson;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.PostJson;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.PutJson;
|
||||
import cn.skcks.matrix.v2.config.swagger.SwaggerConfig;
|
||||
import cn.skcks.matrix.v2.model.role.convert.RoleConvertor;
|
||||
import cn.skcks.matrix.v2.model.role.dto.*;
|
||||
import cn.skcks.matrix.v2.model.role.vo.RoleVo;
|
||||
import cn.skcks.matrix.v2.model.service.ServiceResult;
|
||||
import cn.skcks.matrix.v2.model.user.convert.UserConvertor;
|
||||
import cn.skcks.matrix.v2.model.user.vo.UserVo;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.Role;
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import cn.skcks.matrix.v2.services.casbin.Permission;
|
||||
import cn.skcks.matrix.v2.services.role.RoleService;
|
||||
import cn.skcks.matrix.v2.utils.json.JsonResponse;
|
||||
import cn.skcks.matrix.v2.utils.page.PageWrapper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Auth
|
||||
@Tag(name = "角色")
|
||||
@RestController
|
||||
@JsonMapping("/role")
|
||||
@RequiredArgsConstructor
|
||||
public class RoleController {
|
||||
private final RoleService roleService;
|
||||
private final CasbinService casbinService;
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi roleApi() {
|
||||
return SwaggerConfig.api("Role", "/role");
|
||||
}
|
||||
|
||||
@Operation(summary = "添加角色")
|
||||
@PutJson("/add")
|
||||
public JsonResponse<Void> addRole(@RequestBody @Validated AddRoleParams params) throws Exception {
|
||||
ServiceResult<Void> result = roleService.addRole(params.getRoleName(), params.getRole(), params.getSystem(), params.getPermissions());
|
||||
return result.parseResponse();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除角色")
|
||||
@DeleteJson("/del")
|
||||
public JsonResponse<Void> delRole(@RequestBody @Validated DelRoleParams params) throws Exception {
|
||||
ServiceResult<Void> result = roleService.delRole(params.getRole(), params.getSystem());
|
||||
return result.parseResponse();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询角色表")
|
||||
@PostJson("/list")
|
||||
public JsonResponse<PageWrapper<RoleVo>> roles(@RequestBody @Validated GetRoleParams params) {
|
||||
PageInfo<Role> roles = roleService.getRoles(params.getPage(), params.getSize(), false);
|
||||
return JsonResponse.success(PageWrapper.of(RoleConvertor.INSTANCE.daoToVo(roles)));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询角色表(所有)")
|
||||
@GetJson("/list/all")
|
||||
public JsonResponse<List<RoleVo>> roles() {
|
||||
PageInfo<Role> roles = roleService.getRoles(0, 0, true);
|
||||
return JsonResponse.success(RoleConvertor.INSTANCE.daoToVo(roles).getList());
|
||||
}
|
||||
|
||||
@PostJson("/user/grant")
|
||||
@Operation(summary = "授予用户角色")
|
||||
public JsonResponse<Boolean> grantRoleForUser(@RequestBody @Validated GrantAndRevokeRoleForUserDto dto) {
|
||||
try {
|
||||
roleService.grantRoleForUser(dto.getUserId(), dto.getRole(), dto.getSystem());
|
||||
return JsonResponse.success(true);
|
||||
} catch (Exception e) {
|
||||
return JsonResponse.error(false, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DeleteJson("/user/revoke")
|
||||
@Operation(summary = "撤销用户角色")
|
||||
public JsonResponse<Boolean> revokeRoleForUser(@RequestBody @Validated GrantAndRevokeRoleForUserDto dto) {
|
||||
try {
|
||||
roleService.revokeRoleForUser(dto.getUserId(), dto.getRole(), dto.getSystem());
|
||||
return JsonResponse.success(true);
|
||||
} catch (Exception e) {
|
||||
return JsonResponse.error(false, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@GetJson("/user/roles")
|
||||
@Operation(summary = "获取用户所拥有的角色列表")
|
||||
public JsonResponse<List<String>> getUserRoles(@ParameterObject @Validated GetUserRoles dto) {
|
||||
return JsonResponse.success(roleService.getUserRoles(dto.getUserId(), dto.getSystem()));
|
||||
}
|
||||
|
||||
@GetJson("/user/list")
|
||||
@Operation(summary = "获取角色下所有的用户列表")
|
||||
public JsonResponse<List<UserVo>> getRoleUsers(@ParameterObject @Validated GetRoleUsers dto) {
|
||||
List<UserVo> vos = roleService.getRoleUsers(dto.getRole(), dto.getSystem())
|
||||
.parallelStream()
|
||||
.map(UserConvertor.INSTANCE::daoToVo)
|
||||
.toList();
|
||||
return JsonResponse.success(vos);
|
||||
}
|
||||
|
||||
@GetJson("/user/permissions")
|
||||
@Operation(summary = "用户在某个系统下所拥有的权限列表")
|
||||
public JsonResponse<List<Permission>> getUserPermissions(@ParameterObject @Validated GetUserPermissions dto) {
|
||||
return JsonResponse.success(casbinService.getUserPermission(dto.getUserId(), dto.getSystem()));
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package cn.skcks.matrix.v2.api.test;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.skcks.matrix.v2.annotation.web.JsonMapping;
|
||||
import cn.skcks.matrix.v2.annotation.web.auth.Auth;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.GetJson;
|
||||
import cn.skcks.matrix.v2.annotation.web.methods.PostJson;
|
||||
import cn.skcks.matrix.v2.config.swagger.SwaggerConfig;
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import cn.skcks.matrix.v2.model.test.dto.GetUserParams;
|
||||
import cn.skcks.matrix.v2.model.user.convert.UserConvertor;
|
||||
import cn.skcks.matrix.v2.model.user.vo.UserVo;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import cn.skcks.matrix.v2.services.jwt.JwtService;
|
||||
import cn.skcks.matrix.v2.services.user.UserService;
|
||||
import cn.skcks.matrix.v2.services.route.RouteService;
|
||||
import cn.skcks.matrix.v2.utils.json.JsonResponse;
|
||||
import cn.skcks.matrix.v2.utils.page.PageWrapper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "测试")
|
||||
@RestController
|
||||
@JsonMapping("/test")
|
||||
@RequiredArgsConstructor
|
||||
public class TestController {
|
||||
private final UserService userService;
|
||||
private final RouteService routeService;
|
||||
|
||||
private final JwtService jwtService;
|
||||
|
||||
private final CasbinService casbinService;
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi testApi() {
|
||||
return SwaggerConfig.api("Test", "/test");
|
||||
}
|
||||
|
||||
@Operation(summary = "查询用户表")
|
||||
@PostJson("/users")
|
||||
public JsonResponse<PageWrapper<UserVo>> users(@RequestBody @Validated GetUserParams params) {
|
||||
PageInfo<User> users = userService.getUsers(params.getPage(), params.getSize());
|
||||
return JsonResponse.success(PageWrapper.of(UserConvertor.INSTANCE.daoToVo(users)));
|
||||
}
|
||||
|
||||
@JsonMapping(value = "/route/{*}", method = {RequestMethod.GET})
|
||||
public JsonResponse<RouteInfo> route(HttpServletRequest request) {
|
||||
return JsonResponse.success(routeService.getRouteInfo(request));
|
||||
}
|
||||
|
||||
@Auth
|
||||
@JsonMapping(value = "/routes", method = {RequestMethod.GET})
|
||||
public JsonResponse<Object> routes(HttpServletRequest request) {
|
||||
return JsonResponse.success(routeService.getRoutes());
|
||||
}
|
||||
|
||||
@GetJson("/getToken")
|
||||
public JsonResponse<String> getToken() {
|
||||
return JsonResponse.success(jwtService.generateToken(new Claims(IdUtil.fastUUID(), "test")));
|
||||
}
|
||||
|
||||
@PostJson("/verifyToken")
|
||||
public JsonResponse<Boolean> verifyToken(@RequestParam String token) {
|
||||
return JsonResponse.success(jwtService.verifyToken(token));
|
||||
}
|
||||
|
||||
@PostJson("/parseToken")
|
||||
public JsonResponse<Claims> parseToken(@RequestParam String token) {
|
||||
return JsonResponse.success(jwtService.parseToken(token));
|
||||
}
|
||||
|
||||
@GetJson("/casbin")
|
||||
public JsonResponse<Boolean> casbinTest(@RequestParam String identify,
|
||||
@RequestParam String system,
|
||||
@RequestParam String url,
|
||||
@RequestParam String method) {
|
||||
return JsonResponse.success(casbinService.enforce(identify, system, url, method));
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.skcks.matrix.v2.model.role.convert;
|
||||
|
||||
import cn.skcks.matrix.v2.model.role.vo.RoleVo;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.Role;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@Mapper
|
||||
public abstract class RoleConvertor {
|
||||
public final static RoleConvertor INSTANCE = Mappers.getMapper(RoleConvertor.class);
|
||||
|
||||
abstract public RoleVo daoToVo(Role role);
|
||||
|
||||
abstract public Collection<RoleVo> daoToVo(Collection<Role> roles);
|
||||
|
||||
abstract public PageInfo<RoleVo> daoToVo(PageInfo<Role> pageInfo);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import cn.skcks.matrix.v2.services.role.RoleRoutePermission;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class AddRoleParams {
|
||||
private String roleName;
|
||||
private String role;
|
||||
private String system;
|
||||
private List<RoleRoutePermission> permissions;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DelRoleParams {
|
||||
private String role;
|
||||
private String system;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GetRoleParams {
|
||||
@Schema(description = "页数")
|
||||
@NotNull(message = "page 不能为空")
|
||||
@Min(value = 1, message = "page 必须为正整数")
|
||||
int page;
|
||||
|
||||
@Schema(description = "每页条数", example = "10")
|
||||
@NotNull(message = "size 不能为空")
|
||||
@Min(value = 1, message = "size 必须为正整数")
|
||||
int size;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "获取角色下所有用户")
|
||||
@Data
|
||||
public class GetRoleUsers {
|
||||
@NotBlank(message = "角色 不能为空")
|
||||
@Schema(description = "角色")
|
||||
private String role;
|
||||
|
||||
@NotBlank(message = "系统 不能为空")
|
||||
@Schema(description = "系统", defaultValue = CasbinService.DEFAULT_SYSTEM)
|
||||
private String system;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "用户在某个系统下所拥有的权限")
|
||||
@Data
|
||||
public class GetUserPermissions {
|
||||
@NotBlank(message = "用户id 不能为空")
|
||||
@Pattern(regexp = "\\d*", message = "用户id 格式错误")
|
||||
@Schema(description = "用户id")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "系统 不能为空")
|
||||
@Schema(description = "系统", defaultValue = CasbinService.DEFAULT_SYSTEM)
|
||||
private String system;
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "获取用户所拥有的角色")
|
||||
@Data
|
||||
public class GetUserRoles {
|
||||
@NotBlank(message = "用户id 不能为空")
|
||||
@Pattern(regexp = "\\d*", message = "用户id 格式错误")
|
||||
@Schema(description = "用户id")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "系统 不能为空")
|
||||
@Schema(description = "系统", defaultValue = CasbinService.DEFAULT_SYSTEM)
|
||||
private String system;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.skcks.matrix.v2.model.role.dto;
|
||||
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "授予用户角色")
|
||||
@Data
|
||||
public class GrantAndRevokeRoleForUserDto {
|
||||
@NotBlank(message = "用户id 不能为空")
|
||||
@Pattern(regexp = "\\d*", message = "用户id 格式错误")
|
||||
@Schema(description = "用户id")
|
||||
private String userId;
|
||||
|
||||
@NotBlank(message = "角色 不能为空")
|
||||
@Schema(description = "角色")
|
||||
private String role;
|
||||
|
||||
@NotBlank(message = "系统 不能为空")
|
||||
@Schema(description = "系统", defaultValue = CasbinService.DEFAULT_SYSTEM)
|
||||
private String system;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.skcks.matrix.v2.model.role.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(title = "角色信息")
|
||||
public class RoleVo {
|
||||
@Schema(description = "角色id")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "角色值")
|
||||
private String role;
|
||||
|
||||
@Schema(description = "角色名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "是否启用")
|
||||
private Boolean active;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.skcks.matrix.v2.model.test.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
public class GetUserParams {
|
||||
@Schema(description = "页数")
|
||||
@NotNull(message = "page 不能为空")
|
||||
@Min(value = 1, message = "page 必须为正整数")
|
||||
int page;
|
||||
|
||||
@Schema(description = "每页条数", example = "10")
|
||||
@NotNull(message = "size 不能为空")
|
||||
@Min(value = 1, message = "size 必须为正整数")
|
||||
int size;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.skcks.matrix.v2.model.user.convert;
|
||||
|
||||
import cn.skcks.matrix.v2.model.user.vo.UserVo;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
@Mapper
|
||||
public abstract class UserConvertor {
|
||||
public final static UserConvertor INSTANCE = Mappers.getMapper(UserConvertor.class);
|
||||
|
||||
@Mappings(
|
||||
@Mapping(source = "id", target = "id")
|
||||
)
|
||||
abstract public UserVo daoToVo(User user);
|
||||
|
||||
abstract public Collection<UserVo> daoToVo(Collection<User> users);
|
||||
|
||||
abstract public PageInfo<UserVo> daoToVo(PageInfo<User> userPageInfo);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cn.skcks.matrix.v2.model.user.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Schema(title = "用户信息")
|
||||
public class UserVo {
|
||||
@Schema(description = "用户id")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String userName;
|
||||
|
||||
@Schema(description = "邮箱")
|
||||
private String email;
|
||||
|
||||
@Schema(description = "手机号码")
|
||||
private String phoneNumber;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTime;
|
||||
|
||||
@Schema(description = "最后更新时间")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date updateTime;
|
||||
}
|
@ -249,6 +249,12 @@
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</path>
|
||||
|
||||
<path>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
|
@ -36,6 +36,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.skcks.matrix.v2</groupId>
|
||||
<artifactId>orm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
|
@ -1,7 +0,0 @@
|
||||
package cn.skcks.matrix.v2;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.skcks.matrix.v2.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOriginPatterns("*")
|
||||
.allowCredentials(true)
|
||||
.allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH");
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.skcks.matrix.v2.config;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Slf4j
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "jwt")
|
||||
public class JwtConfig{
|
||||
private final static String DEFAULT_PREFIX = "JWT";
|
||||
private final static String SEPARATOR = ":";
|
||||
|
||||
// 非对称加密算法名称
|
||||
private String algorithm = "ES512";
|
||||
// redis 缓存 公钥 base91 的键名
|
||||
private String publicCacheKey = DEFAULT_PREFIX + SEPARATOR +"PublicCacheKey";
|
||||
// redis 缓存 私钥 base91 的键名
|
||||
private String privateCacheKey = DEFAULT_PREFIX + SEPARATOR +"PrivateCacheKey";
|
||||
// 过期时间 定位:秒
|
||||
private Duration expire = Duration.ofHours(24);
|
||||
// 每次启动都重新生成密钥对
|
||||
private boolean generateKeyPairEveryTime = false;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package cn.skcks.matrix.v2.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(factory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package cn.skcks.matrix.v2.config;
|
||||
|
||||
import cn.skcks.matrix.v2.interceptor.AuthorizationInterceptor;
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import cn.skcks.matrix.v2.services.auth.AuthService;
|
||||
import cn.skcks.matrix.v2.services.jwt.JwtService;
|
||||
import cn.skcks.matrix.v2.utils.json.JsonResponse;
|
||||
import cn.skcks.matrix.v2.utils.json.ResponseStatus;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
private final AuthorizationInterceptor authorizationInterceptor;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final JwtService jwtService;
|
||||
private final AuthService authService;
|
||||
|
||||
private final static JsonResponse<String> NO_LOGIN = JsonResponse.build("未登录", ResponseStatus.UNAUTHORIZED);
|
||||
private final static JsonResponse<String> TOKEN_EXPIRE = JsonResponse.build("认证失效 请重新登录", ResponseStatus.UNAUTHORIZED);
|
||||
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 添加请求 权限校验 拦截器
|
||||
authorizationInterceptor.addAuthHandler(this::auth);
|
||||
|
||||
registry.addInterceptor(authorizationInterceptor)
|
||||
.addPathPatterns("/**")
|
||||
.excludePathPatterns("/error/**", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**");
|
||||
}
|
||||
|
||||
private void writeResponse(HttpServletResponse response, int status, Object data) throws IOException {
|
||||
response.setStatus(status);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
objectMapper.writeValue(response.getWriter(), data);
|
||||
}
|
||||
|
||||
private String getToken(HttpServletRequest request) {
|
||||
return Optional.ofNullable(request.getHeader("token")).orElseGet(() -> {
|
||||
if (request.getCookies() != null) {
|
||||
for (Cookie cookie : request.getCookies()) {
|
||||
if (cookie.getName().equals("token")) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private boolean auth(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
String token = getToken(request);
|
||||
log.info("token => {}", token);
|
||||
|
||||
if (token == null) {
|
||||
writeResponse(response, NO_LOGIN.getCode(), NO_LOGIN);
|
||||
return false;
|
||||
}
|
||||
|
||||
Claims claims = jwtService.parseToken(token);
|
||||
log.info("[解析的令牌信息] {}", claims);
|
||||
if (claims == null) {
|
||||
log.info("[认证失败] 无效 token => {}", token);
|
||||
writeResponse(response, NO_LOGIN.getCode(), NO_LOGIN);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!authService.validateToken(claims, token)) {
|
||||
writeResponse(response, TOKEN_EXPIRE.getCode(), TOKEN_EXPIRE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package cn.skcks.matrix.v2.model.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "用户登录")
|
||||
@Data
|
||||
public class UserLoginDto {
|
||||
@Schema(title = "账号")
|
||||
@NotBlank(message = "账号不能为空")
|
||||
private String account;
|
||||
|
||||
@Schema(title = "密码")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package cn.skcks.matrix.v2.model.auth.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "用户注册")
|
||||
@Data
|
||||
public class UserRegisterDto {
|
||||
@Schema(title = "用户名")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String userName;
|
||||
|
||||
@Schema(title = "密码")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
// @Pattern(
|
||||
// regexp = "^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[^\\w\\s]).{8,}$",
|
||||
// message = "密码必须大于8位 且至少有 一个大写字母 一个小写字符 一个特殊符号")
|
||||
// @Pattern(
|
||||
// regexp = "^(?=.*?[A-z])(?=.*?[0-9])(?=.*?[^\\w\\s]).{8,}$",
|
||||
// message = "密码必须大于8位 且至少有 一个字母 一个特殊符号")
|
||||
@Pattern(
|
||||
regexp = "^(?=.*?[A-z])(?=.*?[0-9]).{6,}$",
|
||||
message = "密码必须大于6位 且至少有 一个字母")
|
||||
private String password;
|
||||
|
||||
@Schema(title = "邮箱")
|
||||
@Email(message = "邮箱格式错误")
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
private String email;
|
||||
|
||||
@Schema(title = "手机号码")
|
||||
private String phoneNumber;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cn.skcks.matrix.v2.model.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(title = "用户登录返回信息")
|
||||
public class UserLoginVo {
|
||||
@Schema(title = "登录状态")
|
||||
private boolean status;
|
||||
|
||||
@Schema(title = "用户id")
|
||||
private String id;
|
||||
|
||||
@Schema(title = "用户令牌")
|
||||
private String token;
|
||||
|
||||
@Schema(title = "刷新令牌")
|
||||
private String refreshToken;
|
||||
|
||||
@Schema(title = "错误信息")
|
||||
private String exception;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.skcks.matrix.v2.model.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(title = "用户注册返回信息")
|
||||
public class UserRegisterVo {
|
||||
@Schema(title = "注册状态")
|
||||
private boolean status;
|
||||
|
||||
@Schema(title = "异常信息")
|
||||
private String exception;
|
||||
|
||||
@Schema(title = "用户id")
|
||||
private String userId;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package cn.skcks.matrix.v2.model.jwt.convert;
|
||||
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
@SuppressWarnings({"unused"})
|
||||
public abstract class ClaimConvertor {
|
||||
public final static ClaimConvertor INSTANCE = Mappers.getMapper(ClaimConvertor.class);
|
||||
|
||||
public Map<String,Object> toMap(Claims claims) {
|
||||
BeanMap beanMap = BeanMap.create(claims);
|
||||
Map<String,Object> map = new HashMap<>(beanMap.size());
|
||||
for(Object key: beanMap.keySet()){
|
||||
map.put(String.valueOf(key),beanMap.get(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public Claims toClaims(Map<String,Object> map) {
|
||||
Claims claims = new Claims();
|
||||
if(map == null){
|
||||
return claims;
|
||||
}
|
||||
|
||||
BeanMap.create(claims).putAll(map);
|
||||
return claims;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package cn.skcks.matrix.v2.model.jwt.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Claims {
|
||||
private String userId;
|
||||
private String userName;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package cn.skcks.matrix.v2.model.route.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(title = "路由信息")
|
||||
@Data
|
||||
@Builder
|
||||
public class RouteInfo {
|
||||
public String controller;
|
||||
public String methodName;
|
||||
public String requestMethod;
|
||||
public String requestUrl;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package cn.skcks.matrix.v2.model.service;
|
||||
|
||||
import cn.skcks.matrix.v2.utils.json.JsonResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Data
|
||||
public class ServiceResult<T> {
|
||||
private final T result;
|
||||
private String exception;
|
||||
private String message;
|
||||
|
||||
public boolean hasException() {
|
||||
return StringUtils.isNotBlank(exception);
|
||||
}
|
||||
|
||||
public boolean hasMessage() {
|
||||
return StringUtils.isNotBlank(message);
|
||||
}
|
||||
|
||||
public JsonResponse<T> parseResponse() {
|
||||
if (hasException()) {
|
||||
return JsonResponse.error(null, exception);
|
||||
} else {
|
||||
if (hasMessage()) {
|
||||
return JsonResponse.success(result, message);
|
||||
} else {
|
||||
return JsonResponse.success(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.skcks.matrix.v2.services.auth;
|
||||
|
||||
import cn.skcks.matrix.v2.model.auth.dto.UserLoginDto;
|
||||
import cn.skcks.matrix.v2.model.auth.dto.UserRegisterDto;
|
||||
import cn.skcks.matrix.v2.model.auth.vo.UserLoginVo;
|
||||
import cn.skcks.matrix.v2.model.auth.vo.UserRegisterVo;
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
|
||||
public interface AuthService {
|
||||
boolean validateToken(String token);
|
||||
|
||||
String generateToken(User user);
|
||||
|
||||
String refreshToken(String refreshToken);
|
||||
|
||||
UserRegisterVo register(UserRegisterDto dto);
|
||||
|
||||
UserLoginVo login(UserLoginDto dto);
|
||||
|
||||
boolean validateToken(Claims claims, String token);
|
||||
void saveToken(User user, String token);
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package cn.skcks.matrix.v2.services.auth;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||
import cn.skcks.matrix.v2.config.JwtConfig;
|
||||
import cn.skcks.matrix.v2.model.auth.dto.UserLoginDto;
|
||||
import cn.skcks.matrix.v2.model.auth.dto.UserRegisterDto;
|
||||
import cn.skcks.matrix.v2.model.auth.vo.UserLoginVo;
|
||||
import cn.skcks.matrix.v2.model.auth.vo.UserRegisterVo;
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import cn.skcks.matrix.v2.services.jwt.JwtService;
|
||||
import cn.skcks.matrix.v2.services.user.UserService;
|
||||
import cn.skcks.matrix.v2.utils.redis.RedisUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AuthServiceImpl implements AuthService {
|
||||
private final JwtConfig jwtConfig;
|
||||
private final JwtService jwtService;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final static String DEFAULT_PREFIX = "AUTH";
|
||||
private final static String SEPARATOR = ":";
|
||||
|
||||
private final static String TOKEN_PREFIX = "TOKEN";
|
||||
|
||||
@Override
|
||||
public boolean validateToken(String token) {
|
||||
return jwtService.verifyToken(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateToken(User user) {
|
||||
Claims claims = new Claims();
|
||||
claims.setUserName(user.getUserName());
|
||||
claims.setUserId(String.valueOf(user.getId()));
|
||||
return jwtService.generateToken(claims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String refreshToken(String refreshToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
protected static class Credentials {
|
||||
private String passwordHex;
|
||||
private String keyHex;
|
||||
}
|
||||
|
||||
protected Credentials createCredentials(String password, byte[] key) {
|
||||
// aes 加密
|
||||
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
|
||||
// 加密后的 hex
|
||||
String passwordHex = aes.encryptHex(password);
|
||||
// 密钥 (hex)
|
||||
String keyHex = HexUtil.encodeHexStr(key);
|
||||
return new Credentials(passwordHex, keyHex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserRegisterVo register(UserRegisterDto dto) {
|
||||
if (userService.exist(dto.getUserName(), dto.getEmail())) {
|
||||
return UserRegisterVo.builder()
|
||||
.status(false)
|
||||
.exception("用户名 或 邮箱已被注册")
|
||||
.build();
|
||||
}
|
||||
|
||||
// 计算 密码的 hash
|
||||
String passwordHash = SecureUtil.sha256(dto.getPassword());
|
||||
|
||||
// 随机生成密钥
|
||||
final byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();
|
||||
final Credentials credentials = createCredentials(passwordHash, key);
|
||||
|
||||
User user = new User();
|
||||
final long id = IdUtil.getSnowflakeNextId();
|
||||
user.setId(id);
|
||||
user.setUserName(dto.getUserName());
|
||||
user.setPassword(credentials.passwordHex);
|
||||
user.setSalt(credentials.keyHex);
|
||||
user.setEmail(dto.getEmail());
|
||||
user.setDescription("");
|
||||
user.setPhoneNumber("");
|
||||
user.setCreateTime(DateUtil.date());
|
||||
user.setUpdateTime(DateUtil.date());
|
||||
|
||||
final boolean status = userService.add(user);
|
||||
|
||||
final UserRegisterVo.UserRegisterVoBuilder builder = UserRegisterVo.builder().status(status);
|
||||
|
||||
if (status) {
|
||||
builder.userId(String.valueOf(id));
|
||||
} else {
|
||||
builder.exception("注册失败");
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserLoginVo login(UserLoginDto dto) {
|
||||
UserLoginVo.UserLoginVoBuilder builder = UserLoginVo.builder().status(false);
|
||||
final String exception = "用户不存在 或 密码错误";
|
||||
if (!userService.exist(dto.getAccount(), dto.getAccount())) {
|
||||
return builder.exception(exception).build();
|
||||
}
|
||||
|
||||
User user = userService.getUser(dto.getAccount());
|
||||
if (user == null) {
|
||||
return builder.exception(exception).build();
|
||||
}
|
||||
|
||||
final String passwordHash = SecureUtil.sha256(dto.getPassword());
|
||||
final byte[] keyHex = HexUtil.decodeHex(user.getSalt());
|
||||
|
||||
Credentials credentials = createCredentials(passwordHash, keyHex);
|
||||
if (!user.getPassword().equals(credentials.getPasswordHex())) {
|
||||
return builder.exception(exception).build();
|
||||
}
|
||||
|
||||
String token = generateToken(user);
|
||||
saveToken(user, token);
|
||||
|
||||
user.setUpdateTime(DateUtil.date());
|
||||
userService.update(user);
|
||||
|
||||
return builder.status(true)
|
||||
.id(String.valueOf(user.getId()))
|
||||
.token(token)
|
||||
.build();
|
||||
}
|
||||
|
||||
private String generateRedisKey(Long id, String token) {
|
||||
return generateRedisKey(String.valueOf(id), token);
|
||||
}
|
||||
|
||||
private String generateRedisKey(String id, String token) {
|
||||
String tokenMd5 = SecureUtil.md5(token);
|
||||
return DEFAULT_PREFIX + SEPARATOR + TOKEN_PREFIX
|
||||
+ SEPARATOR + id
|
||||
+ SEPARATOR + tokenMd5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateToken(Claims claims, String token) {
|
||||
String redisKey = generateRedisKey(claims.getUserId(), token);
|
||||
String cacheToken = RedisUtil.StringOps.get(redisKey);
|
||||
if (StringUtils.isEmpty(cacheToken)) {
|
||||
return false;
|
||||
}
|
||||
return StringUtils.equals(cacheToken, token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveToken(User user, String token) {
|
||||
String redisKey = generateRedisKey(user.getId(), token);
|
||||
RedisUtil.StringOps.set(redisKey, token);
|
||||
RedisUtil.KeyOps.expire(redisKey, jwtConfig.getExpire().getSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package cn.skcks.matrix.v2.services.casbin;
|
||||
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import cn.skcks.matrix.v2.services.role.RoleService;
|
||||
import cn.skcks.matrix.v2.services.route.RouteService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.casbin.jcasbin.main.Enforcer;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Order(10)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CasbinInitService implements CommandLineRunner {
|
||||
private final WebApplicationContext applicationContext;
|
||||
private final List<CasbinRegister> registers = new ArrayList<>();
|
||||
private final RoleService roleService;
|
||||
private final Enforcer enforcer;
|
||||
private final RouteService routeService;
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
// 从 Spring 容器中获取 CasbinRegister
|
||||
applicationContext.getBeansOfType(CasbinRegister.class).forEach((name, clz) -> {
|
||||
log.info("[casbin] 添加 注册器 {}", name);
|
||||
registers.add(clz);
|
||||
});
|
||||
|
||||
List<RouteInfo> routes = routeService.getRoutes();
|
||||
|
||||
// 并行 执行所有注册器
|
||||
registers.parallelStream().forEach(register -> {
|
||||
register.register(enforcer, routes, roleService);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package cn.skcks.matrix.v2.services.casbin;
|
||||
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import cn.skcks.matrix.v2.services.role.RoleService;
|
||||
import org.casbin.jcasbin.main.Enforcer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
||||
public interface CasbinRegister {
|
||||
void register(Enforcer enforcer, List<RouteInfo> routes, RoleService roleService);
|
||||
|
||||
Logger log = LoggerFactory.getLogger(CasbinRegister.class);
|
||||
|
||||
static void addPolicies(Enforcer enforcer, String role, String system, List<RouteInfo> routes) {
|
||||
HashSet<List<String>> params = new HashSet<>(routes.size());
|
||||
for (RouteInfo route : routes) {
|
||||
Permission permission = new Permission(Permission.Identify.ROLE, role, system, route.requestUrl, route.requestMethod);
|
||||
params.add(permission.getPolicyParamList());
|
||||
}
|
||||
|
||||
List<List<String>> existPolicies = enforcer.getFilteredPolicy(0, role, system);
|
||||
|
||||
existPolicies = existPolicies.stream().toList();
|
||||
existPolicies.forEach(existPolicy -> {
|
||||
if (!params.contains(existPolicy)) {
|
||||
log.info("[casbin] {} 移除无效规则 {}", role, existPolicy);
|
||||
enforcer.removePolicy(existPolicy);
|
||||
}
|
||||
});
|
||||
|
||||
// 批量添加规则 enforcer.addPolicies 在部分权限缺失时存在 bug 依然会遗漏缺失的规则 故改为并行单条插入
|
||||
ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors() * 2);
|
||||
pool.submit(()-> params.stream().toList().parallelStream().forEach(enforcer::addPolicy));
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package cn.skcks.matrix.v2.services.casbin;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public interface CasbinService {
|
||||
String SEPARATOR = "::";
|
||||
String USER = "USER";
|
||||
String USER_PREFIX = USER + SEPARATOR;
|
||||
String ROLE = "ROLE";
|
||||
String ROLE_PREFIX = ROLE + SEPARATOR;
|
||||
String DEFAULT_SYSTEM = "WEB";
|
||||
|
||||
String SUPER_PERMISSION = "root";
|
||||
String SUPER_PERMISSION_NAME = "超级管理员";
|
||||
String SUPER_ROLE = ROLE_PREFIX + SUPER_PERMISSION;
|
||||
|
||||
boolean addRole(String role, String system, String url, String method);
|
||||
|
||||
boolean addRole(Permission permission);
|
||||
|
||||
void grantRole(String userId, String role, String system);
|
||||
|
||||
void revokeRole(String userId, String role, String system);
|
||||
|
||||
boolean enforce(String identify, String system, String url, String method);
|
||||
|
||||
List<String> getUserRoles(String userId, String system);
|
||||
|
||||
List<String> getRoleUsers(String role, String system);
|
||||
|
||||
List<Permission> getUserPermission(String userId, String system);
|
||||
|
||||
List<String> getRelationForRoleInSystem(String role, String system);
|
||||
|
||||
boolean delRole(String role, String system);
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package cn.skcks.matrix.v2.services.casbin;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.casbin.jcasbin.main.Enforcer;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CasbinServiceImpl implements CasbinService {
|
||||
private final Enforcer enforcer;
|
||||
|
||||
@Override
|
||||
public boolean addRole(String role, String system, String url, String method) {
|
||||
return addRole(new Permission(Permission.Identify.ROLE, role, system, url, method));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addRole(Permission permission) {
|
||||
if (!permission.getIdentify().equals(Permission.Identify.ROLE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
enforcer.addPolicy(permission.getPolicyParam());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRole(String userId, String role, String system) {
|
||||
enforcer.addRoleForUserInDomain(Permission.Identify.getRoleName(Permission.Identify.USER, userId),
|
||||
Permission.Identify.getRoleName(Permission.Identify.ROLE, role),
|
||||
system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRole(String userId, String role, String system) {
|
||||
enforcer.deleteRoleForUserInDomain(Permission.Identify.getRoleName(Permission.Identify.USER, userId),
|
||||
Permission.Identify.getRoleName(Permission.Identify.ROLE, role),
|
||||
system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enforce(String identify, String system, String url, String method) {
|
||||
return enforcer.enforce(identify, system, url, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserRoles(String userId, String system) {
|
||||
return enforcer.getRolesForUserInDomain(Permission.Identify.getRoleName(Permission.Identify.USER, userId), system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRoleUsers(String role, String system) {
|
||||
return enforcer.getUsersForRoleInDomain(Permission.Identify.getRoleName(Permission.Identify.ROLE, role), system)
|
||||
.parallelStream()
|
||||
.filter(item -> item.startsWith(Permission.Identify.USER.getPrefix()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRelationForRoleInSystem(String role, String system) {
|
||||
return enforcer.getImplicitUsersForRole(Permission.Identify.getRoleName(Permission.Identify.ROLE, role), system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Permission> getUserPermission(String userId, String system) {
|
||||
List<List<String>> permissions = enforcer.getImplicitPermissionsForUserInDomain(Permission.Identify.getRoleName(Permission.Identify.USER, userId), system);
|
||||
return permissions.parallelStream()
|
||||
.map(permission -> new Permission(
|
||||
Permission.Identify.ROLE,
|
||||
permission.get(0),
|
||||
permission.get(1),
|
||||
permission.get(2),
|
||||
permission.get(3)
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delRole(String role, String system) {
|
||||
if (getRelationForRoleInSystem(role, system).size() != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
enforcer.removeFilteredNamedPolicy("p", 0, Permission.Identify.getRoleName(Permission.Identify.ROLE, role), system);
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package cn.skcks.matrix.v2.services.casbin;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Data
|
||||
public class Permission {
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Identify {
|
||||
ROLE(CasbinService.ROLE, CasbinService.ROLE_PREFIX), USER(CasbinService.USER, CasbinService.USER_PREFIX);
|
||||
|
||||
private final String identify;
|
||||
private final String prefix;
|
||||
|
||||
public static String getRoleName(Identify identify, String name) {
|
||||
final String prefix = identify.getPrefix();
|
||||
return getRoleName(prefix, name);
|
||||
}
|
||||
|
||||
public static String getRoleName(String prefix, String name) {
|
||||
return name.startsWith(prefix) ? name : prefix + name;
|
||||
}
|
||||
|
||||
public static String getRawRoleName(String roleName) {
|
||||
for (Identify identify : Identify.values()) {
|
||||
if (roleName.startsWith(identify.getPrefix())) {
|
||||
return StringUtils.removeStart(roleName, identify.getPrefix());
|
||||
}
|
||||
}
|
||||
|
||||
return roleName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return identify;
|
||||
}
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
private Identify identify;
|
||||
|
||||
@JsonIgnore
|
||||
private String name;
|
||||
private String system;
|
||||
private String url;
|
||||
private String method;
|
||||
|
||||
@JsonIgnore
|
||||
public String[] getPolicyParam() {
|
||||
return new String[]{Identify.getRoleName(identify.getPrefix(), name), system, url, method};
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public List<String> getPolicyParamList() {
|
||||
return new ArrayList<>() {{
|
||||
add(Identify.getRoleName(identify.getPrefix(), name));
|
||||
add(system);
|
||||
add(url);
|
||||
add(method);
|
||||
}};
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.skcks.matrix.v2.services.casbin.init;
|
||||
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinRegister;
|
||||
import cn.skcks.matrix.v2.services.role.RoleService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.casbin.jcasbin.main.Enforcer;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.skcks.matrix.v2.services.casbin.CasbinService.*;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SuperRoleRegister implements CasbinRegister {
|
||||
|
||||
@Override
|
||||
public void register(Enforcer enforcer, List<RouteInfo> routes, RoleService roleService) {
|
||||
log.info("[casbin] 初始化 {} 权限", SUPER_PERMISSION_NAME);
|
||||
roleService.initRole(SUPER_PERMISSION, SUPER_PERMISSION_NAME, true);
|
||||
CasbinRegister.addPolicies(enforcer, SUPER_ROLE, DEFAULT_SYSTEM, routes);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cn.skcks.matrix.v2.services.jwt;
|
||||
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
@Service
|
||||
@SuppressWarnings({"unused"})
|
||||
public interface JwtService {
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* 获取公私密钥对
|
||||
* @return 公私密钥对
|
||||
*/
|
||||
KeyPair getKeyPair();
|
||||
|
||||
/**
|
||||
* 获取公钥
|
||||
* @return 公钥
|
||||
*/
|
||||
PublicKey getPublicKey();
|
||||
|
||||
/**
|
||||
* 获取私钥
|
||||
* @return 私钥
|
||||
*/
|
||||
PrivateKey getPrivateKey();
|
||||
|
||||
/**
|
||||
* 重新加载
|
||||
*/
|
||||
void reload();
|
||||
|
||||
String generateToken(Claims claims);
|
||||
|
||||
boolean verifyToken(String token);
|
||||
|
||||
Claims parseToken(String token);
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package cn.skcks.matrix.v2.services.jwt;
|
||||
|
||||
import cn.hutool.core.date.DateField;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.crypto.KeyUtil;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import cn.hutool.jwt.signers.AlgorithmUtil;
|
||||
import cn.hutool.jwt.signers.JWTSigner;
|
||||
import cn.hutool.jwt.signers.JWTSignerUtil;
|
||||
import cn.skcks.matrix.v2.config.JwtConfig;
|
||||
import cn.skcks.matrix.v2.model.jwt.convert.ClaimConvertor;
|
||||
import cn.skcks.matrix.v2.model.jwt.dto.Claims;
|
||||
import cn.skcks.matrix.v2.utils.base91.Base91;
|
||||
import cn.skcks.matrix.v2.utils.redis.RedisUtil;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Data
|
||||
@Order
|
||||
@Service
|
||||
public class JwtServiceImpl implements JwtService, CommandLineRunner {
|
||||
private final JwtConfig jwtConfig;
|
||||
|
||||
private String publicCacheKey;
|
||||
private String privateCacheKey;
|
||||
|
||||
private KeyPair keyPair;
|
||||
|
||||
private PublicKey publicKey;
|
||||
|
||||
private PrivateKey privateKey;
|
||||
|
||||
private boolean checkKeys() {
|
||||
return StringUtils.isNotBlank(RedisUtil.StringOps.get(publicCacheKey))
|
||||
&& StringUtils.isNotBlank(RedisUtil.StringOps.get(privateCacheKey));
|
||||
}
|
||||
|
||||
private boolean hasKeys() {
|
||||
return RedisUtil.KeyOps.hasKey(publicCacheKey)
|
||||
&& RedisUtil.KeyOps.hasKey(privateCacheKey);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
if (!jwtConfig.isGenerateKeyPairEveryTime()) {
|
||||
if (hasKeys() && checkKeys()) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log.info("[JwtService] 每次启动重新生成 jwt 密钥对 已启用");
|
||||
}
|
||||
|
||||
log.info("[JwtService] 生成 jwt 密钥对");
|
||||
final String algorithm = jwtConfig.getAlgorithm();
|
||||
final KeyPair keyPair = KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(algorithm));
|
||||
RedisUtil.StringOps.set(publicCacheKey, Base91.encode(keyPair.getPublic().getEncoded()));
|
||||
RedisUtil.StringOps.set(privateCacheKey, Base91.encode(keyPair.getPrivate().getEncoded()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
log.info("[JwtService] 重新加载密钥对");
|
||||
run();
|
||||
}
|
||||
|
||||
public void loadPublicKey() {
|
||||
final byte[] pub = Base91.decode(RedisUtil.StringOps.get(publicCacheKey));
|
||||
publicKey = KeyUtil.generatePublicKey(AlgorithmUtil.getAlgorithm(jwtConfig.getAlgorithm()), pub);
|
||||
}
|
||||
|
||||
public void loadPrivateKey() {
|
||||
final byte[] pri = Base91.decode(RedisUtil.StringOps.get(privateCacheKey));
|
||||
privateKey = KeyUtil.generatePrivateKey(AlgorithmUtil.getAlgorithm(jwtConfig.getAlgorithm()), pri);
|
||||
}
|
||||
|
||||
public void loadKeyPair() {
|
||||
log.info("[JwtService] 载入密钥对信息");
|
||||
loadPublicKey();
|
||||
loadPrivateKey();
|
||||
keyPair = new KeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
publicCacheKey = jwtConfig.getPublicCacheKey();
|
||||
privateCacheKey = jwtConfig.getPrivateCacheKey();
|
||||
|
||||
log.info("JwtConfig.publicCacheKey {}", publicCacheKey);
|
||||
log.info("JwtConfig.privateCacheKey {}", privateCacheKey);
|
||||
log.info("JwtConfig.expire {}", jwtConfig.getExpire());
|
||||
|
||||
init();
|
||||
loadKeyPair();
|
||||
}
|
||||
|
||||
public JWTSigner getSignSigner() {
|
||||
return JWTSignerUtil.createSigner(jwtConfig.getAlgorithm(), privateKey);
|
||||
}
|
||||
|
||||
private JWTSigner getVerifySigner() {
|
||||
return JWTSignerUtil.createSigner(jwtConfig.getAlgorithm(), keyPair);
|
||||
}
|
||||
|
||||
public String generateToken(Claims claims) {
|
||||
JWTSigner signer = getSignSigner();
|
||||
JWT jwt = JWT.create()
|
||||
.setSigner(signer)
|
||||
.setIssuedAt(DateUtil.date())
|
||||
.setExpiresAt(DateUtil.offset(DateUtil.date(), DateField.SECOND, (int) jwtConfig.getExpire().getSeconds()));
|
||||
Map<String, Object> map = ClaimConvertor.INSTANCE.toMap(claims);
|
||||
map.forEach(jwt::setPayload);
|
||||
return jwt.sign();
|
||||
}
|
||||
|
||||
public boolean verifyToken(String token) {
|
||||
JWTSigner signer = getVerifySigner();
|
||||
|
||||
try {
|
||||
JWT jwt = JWT.of(token).setSigner(signer);
|
||||
return jwt.verify() && jwt.validate(0);
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Claims parseToken(String token) {
|
||||
if (!verifyToken(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
JWT jwt = JWT.of(token).setSigner(getVerifySigner());
|
||||
return ClaimConvertor.INSTANCE.toClaims(jwt.getPayloads().getRaw());
|
||||
} catch (Exception e) {
|
||||
log.warn("{}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package cn.skcks.matrix.v2.services.orm;
|
||||
|
||||
import cn.skcks.matrix.v2.orm.mybatis.operation.OperateTableMapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Order(0)
|
||||
@RequiredArgsConstructor
|
||||
public class OrmService {
|
||||
private final OperateTableMapper mapper;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("[orm] 自动建表");
|
||||
|
||||
log.info("[orm] 创建 User 表");
|
||||
mapper.createUserTable();
|
||||
|
||||
log.info("[orm] 创建 Role 表");
|
||||
mapper.createRoleTable();
|
||||
|
||||
log.info("[orm] 创建 LocationRecord 表");
|
||||
mapper.createLocationRecordTable();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package cn.skcks.matrix.v2.services.role;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RoleRoutePermission {
|
||||
private String url;
|
||||
private String method;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package cn.skcks.matrix.v2.services.role;
|
||||
|
||||
import cn.skcks.matrix.v2.model.service.ServiceResult;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.Role;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public interface RoleService {
|
||||
PageInfo<Role> getRoles(int page, int size, boolean all);
|
||||
|
||||
void initRole(String role, String name, boolean active);
|
||||
|
||||
void grantRoleForUser(String userId, String role, String system) throws Exception;
|
||||
|
||||
void revokeRoleForUser(String userId, String role, String system) throws Exception;
|
||||
|
||||
List<String> getUserRoles(String userId, String system);
|
||||
|
||||
List<User> getRoleUsers(String role, String system);
|
||||
|
||||
ServiceResult<Void> addRole(String roleName, String role, String system, List<RoleRoutePermission> permissions) throws Exception;
|
||||
|
||||
ServiceResult<Void> delRole(String role, String system) throws Exception;
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
package cn.skcks.matrix.v2.services.role;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.skcks.matrix.v2.model.service.ServiceResult;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.mapper.RoleDynamicSqlSupport;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.mapper.RoleMapper;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.mapper.UserDynamicSqlSupport;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.mapper.UserMapper;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.Role;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import cn.skcks.matrix.v2.services.casbin.CasbinService;
|
||||
import cn.skcks.matrix.v2.services.casbin.Permission;
|
||||
import cn.skcks.matrix.v2.services.casbin.Permission.Identify;
|
||||
import cn.skcks.matrix.v2.services.user.UserService;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
|
||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.ForkJoinTask;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class RoleServiceImpl implements RoleService {
|
||||
private final RoleMapper roleMapper;
|
||||
private final CasbinService casbinService;
|
||||
|
||||
private final UserService userService;
|
||||
private final UserMapper userMapper;
|
||||
|
||||
@Override
|
||||
public PageInfo<Role> getRoles(int page, int size, boolean all) {
|
||||
|
||||
PageInfo<Role> pageInfo;
|
||||
if (all) {
|
||||
pageInfo = new PageInfo<>();
|
||||
List<Role> roles = roleMapper.select(u -> u.orderBy(RoleDynamicSqlSupport.id.descending()));
|
||||
pageInfo.setList(roles);
|
||||
pageInfo.setTotal(roles.size());
|
||||
pageInfo.setPageSize(roles.size());
|
||||
pageInfo.setPageNum(1);
|
||||
pageInfo.setPages(1);
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
try (Page<Role> rolePage = PageHelper.startPage(page, size)) {
|
||||
pageInfo = rolePage.doSelectPageInfo(() -> roleMapper.select(u -> u.orderBy(RoleDynamicSqlSupport.id.descending())));
|
||||
}
|
||||
|
||||
List<Role> roles = pageInfo.getList();
|
||||
log.info("共 {} 条, 每页 {} 条, 第 {}/{} 页, {} 条数据",
|
||||
pageInfo.getTotal(),
|
||||
pageInfo.getPageSize(),
|
||||
pageInfo.getPageNum(),
|
||||
pageInfo.getPages(),
|
||||
roles.size());
|
||||
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initRole(String role, String name, boolean active) {
|
||||
Optional<Role> roleModel = roleMapper.selectOne(s -> s
|
||||
.where(RoleDynamicSqlSupport.role.role, SqlBuilder.isEqualTo(role))
|
||||
.and(RoleDynamicSqlSupport.systemId, SqlBuilder.isEqualTo(CasbinService.DEFAULT_SYSTEM))
|
||||
.limit(1));
|
||||
|
||||
if (roleModel.isEmpty()) {
|
||||
Role r = new Role();
|
||||
r.setId(IdUtil.getSnowflakeNextId());
|
||||
r.setActive(active);
|
||||
r.setRole(role);
|
||||
r.setName(name);
|
||||
r.setSystemId(CasbinService.DEFAULT_SYSTEM);
|
||||
roleMapper.insert(r);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean existRole(String role, String system) {
|
||||
Optional<Role> roleModel = roleMapper.selectOne(s -> s
|
||||
.where(RoleDynamicSqlSupport.role.role, SqlBuilder.isEqualTo(role))
|
||||
.and(RoleDynamicSqlSupport.systemId, SqlBuilder.isEqualTo(system))
|
||||
.limit(1));
|
||||
return roleModel.isPresent();
|
||||
}
|
||||
|
||||
private void checkRoleAndUser(String userId, String role, String system) throws Exception {
|
||||
if (!userService.exist(userId)) {
|
||||
throw new Exception("用户不存在");
|
||||
}
|
||||
|
||||
if (!existRole(role, system)) {
|
||||
throw new Exception("角色不存在");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRoleForUser(String userId, String role, String system) throws Exception {
|
||||
checkRoleAndUser(userId, role, system);
|
||||
final String roleName = Identify.getRoleName(Identify.ROLE, role);
|
||||
casbinService.grantRole(userId, roleName, system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRoleForUser(String userId, String role, String system) throws Exception {
|
||||
checkRoleAndUser(userId, role, system);
|
||||
final String roleName = Identify.getRoleName(Identify.ROLE, role);
|
||||
casbinService.revokeRole(userId, roleName, system);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUserRoles(String userId, String system) {
|
||||
return casbinService.getUserRoles(userId, system)
|
||||
.parallelStream()
|
||||
.map(Identify::getRawRoleName)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> getRoleUsers(String role, String system) {
|
||||
List<Long> userIds = casbinService.getRoleUsers(role, system)
|
||||
.parallelStream()
|
||||
.map(Identify::getRawRoleName)
|
||||
.map(Long::valueOf)
|
||||
.toList();
|
||||
if (userIds.size() > 0) {
|
||||
return userMapper.select(u -> u.where(UserDynamicSqlSupport.id, SqlBuilder.isIn(userIds)));
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@ShardingSphereTransactionType
|
||||
public ServiceResult<Void> delRole(String role, String system) throws Exception {
|
||||
ServiceResult.ServiceResultBuilder<Void> result = ServiceResult.builder();
|
||||
if (!existRole(role, system)) {
|
||||
return result.exception(MessageFormat.format("系统 {0} 角色 {1} 不存在", system, role)).build();
|
||||
}
|
||||
|
||||
List<String> relation = casbinService.getRelationForRoleInSystem(role, system);
|
||||
log.info("{} 关联的用户/角色 {}", role, relation);
|
||||
if (relation.size() > 0) {
|
||||
return result.exception(MessageFormat.format("系统 {0} 角色 {1} 已有用户/角色关联", system, role)).build();
|
||||
}
|
||||
|
||||
casbinService.delRole(role, system);
|
||||
|
||||
roleMapper.delete((r) ->
|
||||
r.where(RoleDynamicSqlSupport.role.role, SqlBuilder.isEqualTo(role))
|
||||
.and(RoleDynamicSqlSupport.systemId, SqlBuilder.isEqualTo(system))
|
||||
);
|
||||
return result.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@ShardingSphereTransactionType
|
||||
public ServiceResult<Void> addRole(String roleName, String role, String system, List<RoleRoutePermission> routePermissions) throws Exception {
|
||||
ServiceResult.ServiceResultBuilder<Void> result = ServiceResult.builder();
|
||||
if (existRole(role, system)) {
|
||||
return result.exception(MessageFormat.format("系统 {0} 角色 {1} 已存在", system, role))
|
||||
.build();
|
||||
}
|
||||
|
||||
Role r = new Role();
|
||||
r.setId(IdUtil.getSnowflakeNextId());
|
||||
r.setActive(true);
|
||||
r.setRole(role);
|
||||
r.setName(roleName);
|
||||
r.setSystemId(system);
|
||||
roleMapper.insert(r);
|
||||
|
||||
ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors() * 2);
|
||||
ForkJoinTask<Void> task = pool.submit(() -> {
|
||||
routePermissions.parallelStream()
|
||||
.map(item -> new Permission(Identify.ROLE, role, system, item.getUrl(), item.getMethod()))
|
||||
.forEach(casbinService::addRole);
|
||||
return null;
|
||||
});
|
||||
|
||||
task.get();
|
||||
return result.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package cn.skcks.matrix.v2.services.route;
|
||||
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public interface RouteService{
|
||||
List<RouteInfo> getRoutes();
|
||||
|
||||
@Nullable
|
||||
RouteInfo getRouteInfo(HttpServletRequest request);
|
||||
|
||||
List<RouteInfo> getRouteInfoList(RequestMappingInfo info, HandlerMethod method);
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package cn.skcks.matrix.v2.services.route;
|
||||
|
||||
import cn.skcks.matrix.v2.model.route.dto.RouteInfo;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class RouteServiceImpl implements RouteService {
|
||||
private WebApplicationContext applicationContext;
|
||||
|
||||
public List<RouteInfo> getRouteInfoList(RequestMappingInfo info, HandlerMethod method) {
|
||||
ArrayList<RouteInfo> routes = new ArrayList<>();
|
||||
PathPatternsRequestCondition pathPatternsRequestCondition = info.getPathPatternsCondition();
|
||||
if (pathPatternsRequestCondition == null) {
|
||||
return routes;
|
||||
}
|
||||
|
||||
Set<PathPattern> pathPatterns = pathPatternsRequestCondition.getPatterns();
|
||||
String uri = pathPatterns.size() > 0 ? pathPatterns.stream().toList().get(0).getPatternString() : "";
|
||||
|
||||
RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
|
||||
if (methodsCondition.getMethods().size() == 0) {
|
||||
for (HttpMethod httpMethod : HttpMethod.values()) {
|
||||
RouteInfo route = RouteInfo.builder()
|
||||
.controller(method.getMethod().getDeclaringClass().getName())
|
||||
.methodName(method.getMethod().getName())
|
||||
.requestUrl(uri)
|
||||
.requestMethod(httpMethod.name())
|
||||
.build();
|
||||
routes.add(route);
|
||||
}
|
||||
} else {
|
||||
for (RequestMethod requestMethod : methodsCondition.getMethods()) {
|
||||
RouteInfo route = RouteInfo.builder()
|
||||
.controller(method.getMethod().getDeclaringClass().getName())
|
||||
.methodName(method.getMethod().getName())
|
||||
.requestUrl(uri)
|
||||
.requestMethod(requestMethod.name())
|
||||
.build();
|
||||
routes.add(route);
|
||||
}
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public RouteInfo getRouteInfo(HttpServletRequest request) {
|
||||
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
|
||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
|
||||
RequestMappingInfo info = entry.getKey();
|
||||
HandlerMethod method = entry.getValue();
|
||||
|
||||
// 从请求获取匹配的 RequestMappingInfo
|
||||
RequestMappingInfo matchingInfo = info.getMatchingCondition(request);
|
||||
if (matchingInfo != null) {
|
||||
List<RouteInfo> routeInfos = getRouteInfoList(matchingInfo, method);
|
||||
return routeInfos.size() > 0 ? routeInfos.get(0) : null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RouteInfo> getRoutes() {
|
||||
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
|
||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||
Set<RouteInfo> list = new HashSet<>();
|
||||
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
|
||||
RequestMappingInfo info = entry.getKey();
|
||||
HandlerMethod method = entry.getValue();
|
||||
list.addAll(getRouteInfoList(info, method));
|
||||
}
|
||||
return list.parallelStream().toList();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package cn.skcks.matrix.v2.services.user;
|
||||
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface UserService {
|
||||
PageInfo<User> getUsers(int page, int size);
|
||||
|
||||
@Nullable
|
||||
User getUser(String account);
|
||||
|
||||
boolean exist(String userName, String email);
|
||||
boolean exist(String userId);
|
||||
|
||||
boolean add(User ...users);
|
||||
|
||||
boolean add(User user);
|
||||
|
||||
boolean update(User user);
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package cn.skcks.matrix.v2.services.user;
|
||||
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.mapper.UserDynamicSqlSupport;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.mapper.UserMapper;
|
||||
import cn.skcks.matrix.v2.orm.mybatis.dynamic.model.User;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
|
||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class UserServiceImpl implements UserService {
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public PageInfo<User> getUsers(int page, int size){
|
||||
PageInfo<User> pageInfo;
|
||||
|
||||
try(Page<User> userPage = PageHelper.startPage(page,size)){
|
||||
pageInfo = userPage.doSelectPageInfo(()-> userMapper.select(u->u.orderBy(UserDynamicSqlSupport.id.descending())));
|
||||
}
|
||||
|
||||
List<User> users = pageInfo.getList();
|
||||
log.info("共 {} 条, 每页 {} 条, 第 {}/{} 页, {} 条数据",
|
||||
pageInfo.getTotal(),
|
||||
pageInfo.getPageSize(),
|
||||
pageInfo.getPageNum(),
|
||||
pageInfo.getPages(),
|
||||
users.size());
|
||||
|
||||
return pageInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public User getUser(String account) {
|
||||
if(!exist(account,account)){
|
||||
return null;
|
||||
}
|
||||
|
||||
List<User> users = userMapper.select(u->u.where(UserDynamicSqlSupport.userName, SqlBuilder.isEqualToWhenPresent(account))
|
||||
.or(UserDynamicSqlSupport.email,SqlBuilder.isEqualToWhenPresent(account)));
|
||||
|
||||
if(users.size() != 1){
|
||||
return null;
|
||||
}
|
||||
|
||||
return users.get(0);
|
||||
}
|
||||
|
||||
public boolean exist(String userName,String email){
|
||||
if(StringUtils.isBlank(userName) && StringUtils.isBlank(email)){
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<User> user = userMapper.selectOne(u->u.where(UserDynamicSqlSupport.userName, SqlBuilder.isEqualToWhenPresent(userName))
|
||||
.or(UserDynamicSqlSupport.email,SqlBuilder.isEqualToWhenPresent(email))
|
||||
.limit(1));
|
||||
return user.isPresent();
|
||||
}
|
||||
|
||||
public boolean exist(String userId){
|
||||
if(StringUtils.isBlank(userId)){
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<User> user = userMapper.selectOne(u-> u.where(UserDynamicSqlSupport.id,SqlBuilder.isEqualTo(Long.valueOf(userId)))
|
||||
.limit(1));
|
||||
return user.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@ShardingSphereTransactionType
|
||||
public boolean add(User... users) {
|
||||
return userMapper.insertMultiple(Arrays.asList(users)) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@ShardingSphereTransactionType
|
||||
public boolean add(User user) {
|
||||
return userMapper.insert(user) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(User user) {
|
||||
if(user.getId() == null){
|
||||
return false;
|
||||
}
|
||||
return userMapper.updateByPrimaryKey(user) > 0;
|
||||
}
|
||||
}
|
@ -24,6 +24,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.skcks.matrix.v2</groupId>
|
||||
<artifactId>services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
|
@ -42,7 +42,7 @@ spring:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: 12341234
|
||||
url: jdbc:mysql://10.10.10.200:3306/matrix?createDatabaseIfNotExist=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||
url: jdbc:mysql://10.10.10.200:3306/matrix_v2?createDatabaseIfNotExist=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||
# jdbc-url: jdbc:mysql://10.10.10.100:3306/matrix?createDatabaseIfNotExist=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
|
||||
|
||||
rules:
|
||||
@ -67,6 +67,12 @@ spring:
|
||||
standard:
|
||||
sharding-column: id
|
||||
sharding-algorithm-name: general-id
|
||||
location_record:
|
||||
actual-data-sources: ds
|
||||
sharding-strategy:
|
||||
standard:
|
||||
sharding-column: id
|
||||
sharding-algorithm-name: general-id
|
||||
sharding-algorithms:
|
||||
general-id:
|
||||
# 内置分片算法
|
||||
|
Loading…
Reference in New Issue
Block a user