添加认证/权限接口.

This commit is contained in:
lijiahang
2023-07-17 10:27:53 +08:00
parent 90b9dba7ed
commit 90bf59135f
19 changed files with 437 additions and 96 deletions

View File

@@ -1,10 +1,10 @@
{
"local": {
"baseUrl": "http://127.0.0.1:9200/orion-api",
"token": "Bearer 1"
"token": "Bearer YzIckpmbXSoL6hYUsuqqZHJ8PxUeY19R"
},
"gateway": {
"baseUrl": "http://127.0.0.1:9200/orion-api",
"token": "Bearer 1"
"token": "Bearer YzIckpmbXSoL6hYUsuqqZHJ8PxUeY19R"
}
}

View File

@@ -13,8 +13,14 @@ public interface ErrorMessage {
String ID_MISSING = "id 不能为空";
String INVALID_PARAM = "参数错误";
String DATA_PRESENT = "数据已存在";
String NAME_PRESENT = "名称已存在";
String CODE_PRESENT = "编码已存在";
String DATA_ABSENT = "数据不存在";
String USERNAME_PASSWORD_ERROR = "用户名或密码错误";

View File

@@ -18,6 +18,43 @@ import java.util.Collection;
*/
public interface IMapper<T> extends BaseMapper<T> {
/**
* 获取 ValidateLambdaWrapper 对象
*
* @return 获取 wrapper
*/
default LambdaQueryWrapper<T> wrapper() {
return Conditions.wrapper();
}
/**
* 获取 DataQuery 对象
*
* @return DataQuery
*/
default DataQuery<T> of() {
return DataQuery.of(this);
}
/**
* 获取 DataQuery 对象
*
* @param wrapper wrapper
* @return DataQuery
*/
default DataQuery<T> of(LambdaQueryWrapper<T> wrapper) {
return DataQuery.of(this, wrapper);
}
/**
* 获取 CacheQuery 对象
*
* @return CacheQuery
*/
default CacheQuery<T> cache() {
return CacheQuery.of(this);
}
/**
* 批量插入
*
@@ -91,39 +128,4 @@ public interface IMapper<T> extends BaseMapper<T> {
return Db.saveOrUpdateBatch(entities, size);
}
/**
* @return 获取 wrapper
*/
default LambdaQueryWrapper<T> wrapper() {
return Conditions.wrapper();
}
/**
* 获取 DataQuery 对象
*
* @return DataQuery
*/
default DataQuery<T> of() {
return DataQuery.of(this);
}
/**
* 获取 DataQuery 对象
*
* @param wrapper wrapper
* @return DataQuery
*/
default DataQuery<T> of(LambdaQueryWrapper<T> wrapper) {
return DataQuery.of(this, wrapper);
}
/**
* 获取 CacheQuery 对象
*
* @return CacheQuery
*/
default CacheQuery<T> cache() {
return CacheQuery.of(this);
}
}

View File

@@ -0,0 +1,23 @@
### 登陆 - admin 用户
POST {{baseUrl}}/infra/auth/login
Content-Type: application/json
{
"username": "admin",
"password": "21232f297a57a5a743894a0e4a801fc3"
}
### 登陆
POST {{baseUrl}}/infra/auth/login
Content-Type: application/json
{
"username": "",
"password": ""
}
### 通过 id 更新菜单
GET {{baseUrl}}/infra/auth/logout
Authorization: {{token}}

View File

@@ -9,6 +9,7 @@ import com.orion.ops.module.infra.service.AuthenticationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -40,7 +41,6 @@ public class AuthenticationController {
@PostMapping("/login")
public UserLoginVO login(@Validated @RequestBody UserLoginRequest request,
HttpServletRequest servletRequest) {
// 验证登陆
String token = authenticationService.login(request, servletRequest);
return UserLoginVO.builder().token(token).build();
}
@@ -50,9 +50,22 @@ public class AuthenticationController {
@Operation(summary = "登出")
@GetMapping("/logout")
public HttpWrapper<?> logout(HttpServletRequest servletRequest) {
// 登出
authenticationService.logout(servletRequest);
return HttpWrapper.ok();
}
@Operation(summary = "测试1")
@GetMapping("/test1")
@PreAuthorize("@ss.hasPermission('a')")
public String test1() {
return "123";
}
@Operation(summary = "测试2")
@GetMapping("/test2")
@PreAuthorize("@ss.hasRole('update')")
public String test2() {
return "123";
}
}

View File

@@ -0,0 +1,5 @@
### 初始化缓存
GET {{baseUrl}}/infra/permission/init-cache
Authorization: {{token}}

View File

@@ -0,0 +1,44 @@
package com.orion.ops.module.infra.controller;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.ops.framework.common.annotation.RestWrapper;
import com.orion.ops.module.infra.service.PermissionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 权限服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/14 11:20
*/
@Tag(name = "infra - 权限服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/infra/permission")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class PermissionController {
@Resource
private PermissionService permissionService;
@GetMapping("/init-cache")
@Operation(summary = "初始化缓存")
@PreAuthorize("@ss.hasPermission('infra:system-role:init')")
public HttpWrapper<?> initCache() {
permissionService.initPermissionCache();
return HttpWrapper.ok();
}
}

View File

@@ -2,11 +2,12 @@ package com.orion.ops.module.infra.controller;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.framework.common.annotation.RestWrapper;
import com.orion.ops.module.infra.service.*;
import com.orion.ops.module.infra.entity.vo.*;
import com.orion.ops.module.infra.entity.dto.*;
import com.orion.ops.module.infra.entity.request.*;
import com.orion.ops.module.infra.convert.*;
import com.orion.ops.module.infra.entity.request.SystemRoleCreateRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleQueryRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleStatusRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleUpdateRequest;
import com.orion.ops.module.infra.entity.vo.SystemRoleVO;
import com.orion.ops.module.infra.service.SystemRoleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -51,6 +52,13 @@ public class SystemRoleController {
return systemRoleService.updateSystemRole(request);
}
@PutMapping("/update-status")
@Operation(summary = "通过 id 更新角色状态")
@PreAuthorize("@ss.hasPermission('infra:system-role:update')")
public Integer updateRoleStatus(@Validated @RequestBody SystemRoleStatusRequest request) {
return systemRoleService.updateRoleStatus(request);
}
@GetMapping("/get")
@Operation(summary = "通过 id 查询角色")
@Parameter(name = "id", description = "id", required = true)

View File

@@ -23,6 +23,8 @@ public interface SystemRoleConvert {
SystemRoleDO to(SystemRoleCreateRequest request);
SystemRoleDO to(SystemRoleStatusRequest request);
SystemRoleDO to(SystemRoleUpdateRequest request);
SystemRoleDO to(SystemRoleQueryRequest request);

View File

@@ -0,0 +1,53 @@
package com.orion.ops.module.infra.entity.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 菜单缓存
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/16 1:25
*/
@Data
@Schema(name = "SystemMenuCacheDTO", description = "菜单 缓存业务对象")
public class SystemMenuCacheDTO {
@Schema(description = "id")
private Long id;
@Schema(description = "父id")
private Long parentId;
@Schema(description = "菜单名称")
private String name;
@Schema(description = "菜单权限")
private String permission;
@Schema(description = "菜单类型 1目录 2菜单 3功能")
private Integer type;
@Schema(description = "排序")
private Integer sort;
@Schema(description = "菜单状态 0停用 1启用")
private Integer status;
@Schema(description = "菜单缓存 0不缓存 1缓存")
private Integer cache;
@Schema(description = "菜单图标")
private String icon;
@Schema(description = "路由地址")
private String path;
@Schema(description = "组件名称")
private String componentName;
@Schema(description = "组件地址")
private String component;
}

View File

@@ -0,0 +1,34 @@
package com.orion.ops.module.infra.entity.request;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 角色 更新状态请求对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-7-16 01:19
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "SystemRoleStatusRequest", description = "角色 更新状态请求对象")
public class SystemRoleStatusRequest implements Serializable {
@NotNull
@Schema(description = "id")
private Long id;
@NotNull
@Schema(description = "状态 0停用 1启用")
private Integer status;
}

View File

@@ -28,4 +28,16 @@ public enum RoleStatusEnum {
private final Integer status;
public static RoleStatusEnum of(Integer status) {
if (status == null) {
return null;
}
for (RoleStatusEnum value : values()) {
if (value.status.equals(status)) {
return value;
}
}
return null;
}
}

View File

@@ -1,38 +0,0 @@
package com.orion.ops.module.infra.framework.service.impl;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.security.core.service.SecurityFrameworkService;
import org.springframework.stereotype.Component;
/**
* TODO 依赖用户服务 现在默认实现
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/7 10:57
*/
@Component
public class EmptySecurityImpl implements SecurityFrameworkService {
@Override
public boolean hasPermission(String permission) {
return true;
}
@Override
public boolean hasRole(String role) {
return true;
}
@Override
public LoginUser getUserByToken(String token) {
LoginUser user = new LoginUser();
user.setId(123L);
user.setUsername("username");
user.setNickname("nickname");
user.setRoles(Lists.of("r1", "r2"));
return user;
}
}

View File

@@ -0,0 +1,92 @@
package com.orion.ops.module.infra.framework.service.impl;
import com.orion.lang.utils.time.Dates;
import com.orion.ops.framework.common.constant.ErrorCode;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.security.core.service.SecurityFrameworkService;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
import com.orion.ops.module.infra.enums.LoginTokenStatusEnum;
import com.orion.ops.module.infra.service.AuthenticationService;
import com.orion.ops.module.infra.service.PermissionService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* 安全包配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/7 10:57
*/
@Service
public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Resource
private AuthenticationService authenticationService;
@Resource
private PermissionService permissionService;
@Override
public boolean hasPermission(String permission) {
List<String> roles = Optional.ofNullable(SecurityUtils.getLoginUser())
.map(LoginUser::getRoles)
.orElse(null);
if (roles == null) {
return false;
}
// 检查是否有权限
return permissionService.rolesHasPermission(roles, permission);
}
@Override
public boolean hasRole(String role) {
List<String> roles = Optional.ofNullable(SecurityUtils.getLoginUser())
.map(LoginUser::getRoles)
.orElse(null);
if (roles == null) {
return false;
}
// 检查是否有角色
return permissionService.rolesHasRole(roles, role);
}
@Override
public LoginUser getUserByToken(String token) {
// 获取 token 信息
LoginTokenDTO tokenInfo = authenticationService.getLoginTokenInfo(token, true);
if (tokenInfo == null) {
return null;
}
// 检查 token 状态
this.checkTokenStatus(tokenInfo);
// 获取登陆信息
return authenticationService.getLoginUser(tokenInfo.getId());
}
/**
* 检查 token 状态
*
* @param loginToken loginToken
*/
private void checkTokenStatus(LoginTokenDTO loginToken) {
Integer tokenStatus = loginToken.getTokenStatus();
// 正常状态
if (LoginTokenStatusEnum.OK.getStatus().equals(tokenStatus)) {
return;
}
// 其他设备登陆
if (LoginTokenStatusEnum.OTHER_DEVICE.getStatus().equals(tokenStatus)) {
throw ErrorCode.OTHER_DEVICE_LOGIN.exception(
Dates.format(new Date(loginToken.getLoginTime()), Dates.MD_HM),
loginToken.getIp(),
loginToken.getLocation());
}
}
}

View File

@@ -1,6 +1,7 @@
package com.orion.ops.module.infra.service;
import com.orion.lang.define.wrapper.Pair;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
import com.orion.ops.module.infra.entity.request.UserLoginRequest;
import javax.servlet.http.HttpServletRequest;
@@ -31,11 +32,20 @@ public interface AuthenticationService {
void logout(HttpServletRequest servletRequest);
/**
* 获取 token pair
* 获取登陆用户信息
*
* @param loginToken loginToken
* @return pair
* @param userId userId
* @return loginUser
*/
Pair<Long, Long> getLoginTokenPair(String loginToken);
LoginUser getLoginUser(Long userId);
/**
* 获取 token 信息
*
* @param loginToken loginToken
* @param checkRefresh 是否检查 refreshToken
* @return tokenInfo
*/
LoginTokenDTO getLoginTokenInfo(String loginToken, boolean checkRefresh);
}

View File

@@ -1,6 +1,10 @@
package com.orion.ops.module.infra.service;
import com.orion.ops.module.infra.entity.domain.SystemRoleDO;
import com.orion.ops.module.infra.entity.dto.SystemMenuCacheDTO;
import java.util.List;
import java.util.Map;
/**
* 权限服务
@@ -11,6 +15,32 @@ import java.util.List;
*/
public interface PermissionService {
/**
* 获取 菜单缓存
*
* @return cache
*/
Map<String, SystemRoleDO> getRoleCache();
/**
* 获取 菜单缓存 以作角色权限直接引用
*
* @return cache
*/
List<SystemMenuCacheDTO> getMenuCache();
/**
* 获取 角色菜单关联
*
* @return cache
*/
Map<String, List<SystemMenuCacheDTO>> getRoleMenuCache();
/**
* 初始化权限缓存
*/
void initPermissionCache();
/**
* 检查角色是否含有此角色 (有效性判断)
*

View File

@@ -1,10 +1,11 @@
package com.orion.ops.module.infra.service;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.module.infra.entity.vo.*;
import com.orion.ops.module.infra.entity.dto.*;
import com.orion.ops.module.infra.entity.request.*;
import com.orion.ops.module.infra.convert.*;
import com.orion.ops.module.infra.entity.request.SystemRoleCreateRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleQueryRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleStatusRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleUpdateRequest;
import com.orion.ops.module.infra.entity.vo.SystemRoleVO;
import java.util.List;
@@ -33,6 +34,14 @@ public interface SystemRoleService {
*/
Integer updateSystemRole(SystemRoleUpdateRequest request);
/**
* 更新角色状态
*
* @param request request
* @return effect
*/
Integer updateRoleStatus(SystemRoleStatusRequest request);
/**
* 通过 id 查询角色
*

View File

@@ -65,13 +65,14 @@ public class PermissionServiceImpl implements PermissionService {
@Resource
private SystemRoleMenuDAO systemRoleMenuDAO;
/**
* 初始化缓存
*/
@PostConstruct
public void initRoleMenuCache() {
@Override
public void initPermissionCache() {
long start = System.currentTimeMillis();
log.info("initRoleMenuCache-start");
roleCache.clear();
menuCache.clear();
roleMenuCache.clear();
// 加载所有角色
List<SystemRoleDO> roles = systemRoleDAO.selectList(null);
Map<Long, SystemRoleDO> roleRel = roles.stream()

View File

@@ -11,8 +11,10 @@ import com.orion.ops.module.infra.dao.SystemRoleDAO;
import com.orion.ops.module.infra.entity.domain.SystemRoleDO;
import com.orion.ops.module.infra.entity.request.SystemRoleCreateRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleQueryRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleStatusRequest;
import com.orion.ops.module.infra.entity.request.SystemRoleUpdateRequest;
import com.orion.ops.module.infra.entity.vo.SystemRoleVO;
import com.orion.ops.module.infra.enums.RoleStatusEnum;
import com.orion.ops.module.infra.service.PermissionService;
import com.orion.ops.module.infra.service.SystemRoleService;
import lombok.extern.slf4j.Slf4j;
@@ -20,6 +22,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* 角色 服务实现类
@@ -63,7 +66,6 @@ public class SystemRoleServiceImpl implements SystemRoleService {
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
SystemRoleDO updateRecord = SystemRoleConvert.MAPPER.to(request);
// 查询数据是否冲突
// 查询名称是否存在
this.checkNamePresent(updateRecord);
// 查询编码是否存在
@@ -77,6 +79,25 @@ public class SystemRoleServiceImpl implements SystemRoleService {
return effect;
}
@Override
public Integer updateRoleStatus(SystemRoleStatusRequest request) {
// 查询
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
SystemRoleDO record = systemRoleDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 转换
SystemRoleDO updateRecord = SystemRoleConvert.MAPPER.to(request);
Integer status = updateRecord.getStatus();
Valid.notNull(RoleStatusEnum.of(status), ErrorMessage.INVALID_PARAM);
// 更新
int effect = systemRoleDAO.updateById(updateRecord);
log.info("SystemRoleService-updateRoleStatus effect: {}, updateRecord: {}", effect, JSON.toJSONString(updateRecord));
// 修改缓存状态
SystemRoleDO roleCache = permissionService.getRoleCache().get(record.getCode());
roleCache.setStatus(status);
return effect;
}
@Override
public SystemRoleVO getSystemRole(Long id) {
// 查询
@@ -117,6 +138,13 @@ public class SystemRoleServiceImpl implements SystemRoleService {
public Integer deleteSystemRole(Long id) {
int effect = systemRoleDAO.deleteById(id);
log.info("SystemRoleService-deleteSystemRole id: {}, effect: {}", id, effect);
// 删除缓存
Map<String, SystemRoleDO> roleCache = permissionService.getRoleCache();
roleCache.values()
.stream()
.filter(s -> s.getId().equals(id))
.findFirst()
.ifPresent(s -> roleCache.remove(s.getCode()));
return effect;
}
@@ -124,6 +152,13 @@ public class SystemRoleServiceImpl implements SystemRoleService {
public Integer batchDeleteSystemRole(List<Long> idList) {
int effect = systemRoleDAO.deleteBatchIds(idList);
log.info("SystemRoleService-batchDeleteSystemRole idList: {}, effect: {}", JSON.toJSONString(idList), effect);
// 删除缓存
Map<String, SystemRoleDO> roleCache = permissionService.getRoleCache();
roleCache.values()
.stream()
.filter(s -> idList.contains(s.getId()))
.map(SystemRoleDO::getCode)
.forEach(roleCache::remove);
return effect;
}