Commit 502edea0 by Lizh

增加菜单权限校验逻辑

parent f2e62a8a
package com.jomalls.custom.app.annotation;
import java.lang.annotation.*;
/**
* @Author: Lizh
* @Date: 2026/6/2 10:58
* @Description: 需要验证重复提交的注解
* @Version: 1.0
*/
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
public int interval() default 5000;
/**
* 提示消息
*/
public String message() default "不允许重复提交,请稍候再试";
}
package com.jomalls.custom.app.aspect;
import com.jomalls.custom.app.annotation.RequiresPermissions;
import com.jomalls.custom.app.enums.CodeEnum;
import com.jomalls.custom.app.exception.ServiceException;
import com.jomalls.custom.app.service.PermissionService;
import org.aspectj.lang.JoinPoint;
......@@ -65,7 +66,7 @@ public class PermissionAspect {
if (!hasPermission) {
log.warn("用户无权限访问,权限标识: {},方法: {}", permission, method.getName());
throw new ServiceException(message, 403);
throw new ServiceException(message, CodeEnum.FORBIDDEN.getCode());
}
}
}
package com.jomalls.custom.app.constant;
/**
* 安全常量定义
*/
public class SecurityConstants {
/**
* 超级管理员用户ID
*/
public static final Long ADMIN_USER_ID = 1L;
/**
* 超级管理员角色标识
*/
public static final String SUPER_ADMIN_ROLE = "admin";
/**
* 所有权限标识(通配符)
*/
public static final String ALL_PERMISSION = "*:*:*";
/**
* 权限分隔符
*/
public static final String PERMISSION_DELIMETER = ",";
}
\ No newline at end of file
package com.jomalls.custom.app.exception;
import lombok.Getter;
import java.io.Serial;
/**
* 权限不足异常
* 用于权限校验失败时抛出
*/
@Getter
public class PermissionDeniedException extends RuntimeException {
@Serial
private static final long serialVersionUID = 1L;
private Integer code = 403;
public PermissionDeniedException() {
super("无访问权限");
}
public PermissionDeniedException(String message) {
super(message);
}
public PermissionDeniedException(Integer code, String message) {
super(message);
this.code = code;
}
}
\ No newline at end of file
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.model.SysRoleDeptVO;
import com.jomalls.custom.app.model.SysRoleDeptPageVO;
import com.jomalls.custom.app.vo.SysRoleDeptVO;
import com.jomalls.custom.app.vo.SysRoleDeptPageVO;
import java.util.List;
......
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.model.SysRoleMenuVO;
import com.jomalls.custom.app.model.SysRoleMenuPageVO;
import com.jomalls.custom.app.vo.SysRoleMenuVO;
import com.jomalls.custom.app.vo.SysRoleMenuPageVO;
import java.util.List;
......
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.model.SysRoleVO;
import com.jomalls.custom.app.model.SysRolePageVO;
import com.jomalls.custom.app.vo.SysRoleVO;
import com.jomalls.custom.app.vo.SysRolePageVO;
import java.util.List;
......
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.model.SysUserOldVO;
import com.jomalls.custom.app.model.SysUserOldPageVO;
import com.jomalls.custom.app.vo.SysUserOldVO;
import com.jomalls.custom.app.vo.SysUserOldPageVO;
import java.util.List;
......
package com.jomalls.custom.app.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jomalls.custom.app.model.SysUserRoleVO;
import com.jomalls.custom.app.model.SysUserRolePageVO;
import com.jomalls.custom.app.vo.SysUserRoleVO;
import com.jomalls.custom.app.vo.SysUserRolePageVO;
import java.util.List;
......
package com.jomalls.custom.app.service.impl;
import com.jomalls.custom.app.constant.SecurityConstants;
import com.jomalls.custom.app.service.PermissionService;
import com.jomalls.custom.domain.service.SysMenuDomainService;
import com.jomalls.custom.domain.service.SysRoleDomainService;
import com.jomalls.custom.security.LoginUser;
import com.jomalls.custom.security.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.StringUtils;
import java.util.HashSet;
......@@ -18,28 +20,15 @@ import java.util.Set;
/**
* 权限服务实现
*/
@Slf4j
@Service
public class PermissionServiceImpl implements PermissionService {
private static final Logger log = LoggerFactory.getLogger(PermissionServiceImpl.class);
@Autowired
private SysMenuDomainService sysMenuDomainService;
/**
* 所有权限标识
*/
private static final String ALL_PERMISSION = "*:*:*";
/**
* 权限分隔符
*/
private static final String PERMISSION_DELIMETER = ",";
/**
* 管理员角色标识
*/
private static final String ADMIN_ROLE = "admin";
@Autowired
private SysRoleDomainService sysRoleDomainService;
@Override
public boolean hasPermi(String permission) {
......@@ -50,6 +39,11 @@ public class PermissionServiceImpl implements PermissionService {
if (loginUser == null) {
return false;
}
if (SecurityConstants.ADMIN_USER_ID.equals(loginUser.getUserId())) {
return true;
}
Set<String> permissions = getPermissionsFromCache(loginUser);
return hasPermissions(permissions, permission);
}
......@@ -68,8 +62,13 @@ public class PermissionServiceImpl implements PermissionService {
if (loginUser == null) {
return false;
}
if (SecurityConstants.ADMIN_USER_ID.equals(loginUser.getUserId())) {
return true;
}
Set<String> authorities = getPermissionsFromCache(loginUser);
for (String permission : permissions.split(PERMISSION_DELIMETER)) {
for (String permission : permissions.split(SecurityConstants.PERMISSION_DELIMETER)) {
if (permission != null && hasPermissions(authorities, permission.trim())) {
return true;
}
......@@ -86,8 +85,12 @@ public class PermissionServiceImpl implements PermissionService {
if (loginUser == null) {
return false;
}
// 简单实现:检查是否是管理员
return ADMIN_ROLE.equalsIgnoreCase(role);
if (SecurityConstants.ADMIN_USER_ID.equals(loginUser.getUserId())) {
return true;
}
return hasUserRole(loginUser.getUserId(), role);
}
@Override
......@@ -100,54 +103,59 @@ public class PermissionServiceImpl implements PermissionService {
if (!StringUtils.hasText(roles)) {
return false;
}
for (String role : roles.split(PERMISSION_DELIMETER)) {
if (hasRole(role.trim())) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
return false;
}
if (SecurityConstants.ADMIN_USER_ID.equals(loginUser.getUserId())) {
return true;
}
for (String role : roles.split(SecurityConstants.PERMISSION_DELIMETER)) {
if (hasUserRole(loginUser.getUserId(), role.trim())) {
return true;
}
}
return false;
}
/**
* 从缓存或数据库获取用户权限
*
* @param loginUser 登录用户
* @return 权限集合
*/
private boolean hasUserRole(Long userId, String role) {
try {
List<String> userRoles = sysRoleDomainService.selectRoleKeysByUserId(userId);
if (!CollectionUtils.isEmpty(userRoles)) {
return userRoles.stream()
.anyMatch(r -> SecurityConstants.SUPER_ADMIN_ROLE.equals(r)
|| PatternMatchUtils.simpleMatch(r, role));
}
} catch (Exception e) {
log.error("查询用户角色异常: {}", e.getMessage());
}
return false;
}
private Set<String> getPermissionsFromCache(LoginUser loginUser) {
// 首先检查缓存的权限
if (!CollectionUtils.isEmpty(loginUser.getPermissions())) {
return loginUser.getPermissions();
}
// 如果缓存中没有,从数据库查询
Set<String> permissions = queryUserPermissions(loginUser.getUserId());
// 更新缓存
loginUser.setPermissions(permissions);
SecurityUtils.setLoginUser(loginUser);
return permissions;
}
/**
* 查询用户权限
*
* @param userId 用户ID
* @return 权限集合
*/
private Set<String> queryUserPermissions(Long userId) {
Set<String> permissions = new HashSet<>();
try {
// 根据用户ID查询权限字符串列表
List<String> permsList = sysMenuDomainService.selectMenuPermsByUserId(userId);
// 过滤空权限并添加到集合
for (String perms : permsList) {
if (StringUtils.hasText(perms)) {
// 处理可能包含逗号分隔的权限字符串
String[] permsArray = perms.split(PERMISSION_DELIMETER);
String[] permsArray = perms.split(SecurityConstants.PERMISSION_DELIMETER);
for (String perm : permsArray) {
if (StringUtils.hasText(perm)) {
permissions.add(perm.trim());
......@@ -164,14 +172,19 @@ public class PermissionServiceImpl implements PermissionService {
return permissions;
}
/**
* 判断是否包含权限
*
* @param permissions 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
private boolean hasPermissions(Set<String> permissions, String permission) {
return permissions.contains(ALL_PERMISSION) || permissions.contains(permission.trim());
if (CollectionUtils.isEmpty(permissions) || !StringUtils.hasText(permission)) {
return false;
}
String targetPermission = permission.trim();
if (permissions.contains(SecurityConstants.ALL_PERMISSION)) {
return true;
}
return permissions.stream()
.filter(StringUtils::hasText)
.anyMatch(perm -> PatternMatchUtils.simpleMatch(perm, targetPermission));
}
}
}
\ No newline at end of file
......@@ -8,8 +8,8 @@ import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.SysRoleDeptEntity;
import com.jomalls.custom.domain.service.SysRoleDeptDomainService;
import com.jomalls.custom.app.model.SysRoleDeptVO;
import com.jomalls.custom.app.model.SysRoleDeptPageVO;
import com.jomalls.custom.app.vo.SysRoleDeptVO;
import com.jomalls.custom.app.vo.SysRoleDeptPageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
......
......@@ -8,8 +8,8 @@ import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.SysRoleMenuEntity;
import com.jomalls.custom.domain.service.SysRoleMenuDomainService;
import com.jomalls.custom.app.model.SysRoleMenuVO;
import com.jomalls.custom.app.model.SysRoleMenuPageVO;
import com.jomalls.custom.app.vo.SysRoleMenuVO;
import com.jomalls.custom.app.vo.SysRoleMenuPageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
......
......@@ -8,8 +8,8 @@ import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.SysRoleEntity;
import com.jomalls.custom.domain.service.SysRoleDomainService;
import com.jomalls.custom.app.model.SysRoleVO;
import com.jomalls.custom.app.model.SysRolePageVO;
import com.jomalls.custom.app.vo.SysRoleVO;
import com.jomalls.custom.app.vo.SysRolePageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
......
......@@ -8,8 +8,8 @@ import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.SysUserOldEntity;
import com.jomalls.custom.domain.service.SysUserOldDomainService;
import com.jomalls.custom.app.model.SysUserOldVO;
import com.jomalls.custom.app.model.SysUserOldPageVO;
import com.jomalls.custom.app.vo.SysUserOldVO;
import com.jomalls.custom.app.vo.SysUserOldPageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
......
......@@ -8,8 +8,8 @@ import com.jomalls.custom.app.utils.BeanMapper;
import com.jomalls.custom.app.utils.CustomAsserts;
import com.jomalls.custom.dal.entity.SysUserRoleEntity;
import com.jomalls.custom.domain.service.SysUserRoleDomainService;
import com.jomalls.custom.app.model.SysUserRoleVO;
import com.jomalls.custom.app.model.SysUserRolePageVO;
import com.jomalls.custom.app.vo.SysUserRoleVO;
import com.jomalls.custom.app.vo.SysUserRolePageVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
......
package com.jomalls.custom.app.utils;
import com.jomalls.custom.app.constant.CodeEnum;
import com.jomalls.custom.app.enums.CodeEnum;
import lombok.Getter;
import lombok.Setter;
......
package com.jomalls.custom.app.utils;
import jakarta.servlet.http.HttpServletRequest;
/**
* @Author: Lizh
* @Date: 2026/6/2 10:26
* @Description:
* @Version: 1.0
*/
public class RequestHolder {
private static final ThreadLocal<HttpServletRequest> REQUEST_CONTEXT_HOLDER = new InheritableThreadLocal<>();
public RequestHolder() {
}
public static HttpServletRequest getRequestHolder() {
return REQUEST_CONTEXT_HOLDER.get();
}
public static void setRequestHolder(HttpServletRequest request) {
REQUEST_CONTEXT_HOLDER.set(request);
}
public static void removeRequestHolder() {
REQUEST_CONTEXT_HOLDER.remove();
}
}
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import lombok.*;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import lombok.*;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
......@@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
/**
* 角色信息表 Model
......@@ -109,5 +110,10 @@ public class SysRoleVO implements Serializable {
@Schema(description = "备注")
private String remark;
/**
* 权限集合
*/
@Schema(description = "权限集合")
private Set<String> permissions;
}
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import com.jomalls.custom.page.PageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
......
package com.jomalls.custom.app.model;
package com.jomalls.custom.app.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
......
......@@ -5,6 +5,8 @@ import lombok.*;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
/**
......@@ -142,5 +144,10 @@ public class SysUserVO implements Serializable {
@Schema(description = "备注")
private String remark;
/**
* 角色列表
*/
@Schema(description = "角色列表")
private List<SysRoleVO> roles;
}
package com.jomalls.custom.security;
import lombok.Getter;
import lombok.Setter;
import java.io.Serial;
import java.io.Serializable;
import java.util.Set;
/**
* 登录用户身份权限
*/
@Setter
@Getter
public class LoginUser implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
......@@ -74,91 +81,4 @@ public class LoginUser implements Serializable {
this.username = username;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getDeptId() {
return deptId;
}
public void setDeptId(Long deptId) {
this.deptId = deptId;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Long getLoginTime() {
return loginTime;
}
public void setLoginTime(Long loginTime) {
this.loginTime = loginTime;
}
public Long getExpireTime() {
return expireTime;
}
public void setExpireTime(Long expireTime) {
this.expireTime = expireTime;
}
public String getIpaddr() {
return ipaddr;
}
public void setIpaddr(String ipaddr) {
this.ipaddr = ipaddr;
}
public String getLoginLocation() {
return loginLocation;
}
public void setLoginLocation(String loginLocation) {
this.loginLocation = loginLocation;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
......@@ -4,6 +4,8 @@ import com.jomalls.custom.dal.entity.SysMenuEntity;
import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author Lizh
* @version 0.01
......@@ -12,4 +14,5 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Mapper
public interface SysMenuMapper extends BaseMapper<SysMenuEntity> {
List<String> selectMenuPermsByUserId(Long userId);
}
......@@ -4,6 +4,8 @@ import com.jomalls.custom.dal.entity.SysRoleEntity;
import com.jomalls.custom.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author Lizh
* @version 0.01
......@@ -12,4 +14,5 @@ import org.apache.ibatis.annotations.Mapper;
*/
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRoleEntity> {
List<String> selectRoleKeysByUserId(Long userId);
}
......@@ -3,6 +3,8 @@ package com.jomalls.custom.domain.service;
import com.jomalls.custom.dal.entity.SysRoleEntity;
import com.jomalls.custom.service.IBaseService;
import java.util.List;
/**
* @author Lizh
* @version 0.01
......@@ -11,5 +13,12 @@ import com.jomalls.custom.service.IBaseService;
*/
public interface SysRoleDomainService extends IBaseService<SysRoleEntity> {
/**
* 根据用户ID查询角色标识列表
*
* @param userId 用户ID
* @return 角色标识列表
*/
List<String> selectRoleKeysByUserId(Long userId);
}
package com.jomalls.custom.domain.service.impl;
import com.jomalls.custom.dal.mapper.SysMenuMapper;
import com.jomalls.custom.dal.entity.SysMenuEntity;
import com.jomalls.custom.dal.mapper.SysMenuMapper;
import com.jomalls.custom.domain.service.SysMenuDomainService;
import com.jomalls.custom.service.impl.BaseServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
......@@ -28,9 +26,9 @@ public class SysMenuDomainServiceImpl extends BaseServiceImpl<SysMenuMapper,SysM
}
// 自定义方法或者基础方法重写
@Override
public List<String> selectMenuPermsByUserId(Long userId) {
//return baseMapper.selectMenuPermsByUserId(userId);
return null;
return baseMapper.selectMenuPermsByUserId(userId);
}
}
\ No newline at end of file
......@@ -8,6 +8,8 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Lizh
......@@ -22,6 +24,9 @@ public class SysRoleDomainServiceImpl extends BaseServiceImpl<SysRoleMapper,SysR
public SysRoleDomainServiceImpl(SqlSessionFactory sqlSessionFactory) {
super(sqlSessionFactory);
}
// 自定义方法或者基础方法重写
@Override
public List<String> selectRoleKeysByUserId(Long userId) {
return baseMapper.selectRoleKeysByUserId(userId);
}
}
\ No newline at end of file
......@@ -49,4 +49,17 @@
update_time,
remark,
</sql>
<!-- 根据用户ID查询权限列表 -->
<select id="selectMenuPermsByUserId" parameterType="Long" resultType="String">
SELECT DISTINCT m.perms
FROM sys_user_role ur
LEFT JOIN sys_role_menu rm ON ur.role_id = rm.role_id
LEFT JOIN sys_menu m ON rm.menu_id = m.menu_id
WHERE ur.user_id = #{userId}
AND m.del_flag = '0'
AND m.status = '0'
AND m.perms IS NOT NULL
AND m.perms != ''
</select>
</mapper>
......@@ -37,4 +37,12 @@
update_time,
remark,
</sql>
<!-- 根据用户ID查询角色标识列表 -->
<select id="selectRoleKeysByUserId" parameterType="Long" resultType="String">
SELECT r.role_key
FROM sys_user_role ur
LEFT JOIN sys_role r ON ur.role_id = r.role_id
WHERE ur.user_id = #{userId} AND r.del_flag = '0' AND r.status = '0'
</select>
</mapper>
......@@ -58,7 +58,11 @@
<version>8.3.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
......
package com.jomalls.custom.config;
import com.jomalls.custom.app.enums.CodeEnum;
import com.jomalls.custom.app.exception.PermissionDeniedException;
import com.jomalls.custom.app.exception.ServiceException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......@@ -9,9 +11,23 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class CommonExceptionHandlerAdvice {
/**
* 权限异常处理(返回403 Forbidden)
*/
@ExceptionHandler(PermissionDeniedException.class)
public ResponseEntity<com.jomalls.custom.app.utils.R<Object>> handlePermissionDeniedException(PermissionDeniedException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(com.jomalls.custom.app.utils.R.fail(CodeEnum.FORBIDDEN.getCode(), e.getMessage()));
}
/**
* 业务异常处理
*/
@ExceptionHandler(ServiceException.class)
public ResponseEntity<com.jomalls.custom.app.utils.R<Object>> handleServiceException(ServiceException e) {
return ResponseEntity.ok(com.jomalls.custom.app.utils.R.fail(e.getMessage()));
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(com.jomalls.custom.app.utils.R.fail(e.getCode(), e.getMessage()));
}
@ExceptionHandler(RuntimeException.class)
......@@ -23,6 +39,6 @@ public class CommonExceptionHandlerAdvice {
@ExceptionHandler(Exception.class)
public ResponseEntity<com.jomalls.custom.app.utils.R<Object>> handleException(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(com.jomalls.custom.app.utils.R.fail(com.jomalls.custom.app.constant.CodeEnum.FAIL));
.body(com.jomalls.custom.app.utils.R.fail(CodeEnum.FAIL));
}
}
\ No newline at end of file
package com.jomalls.custom.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jomalls.custom.app.constant.CodeEnum;
import com.jomalls.custom.app.enums.CodeEnum;
import org.jspecify.annotations.NonNull;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
......
......@@ -9,6 +9,7 @@ import org.jspecify.annotations.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
......@@ -27,12 +28,19 @@ public class SecurityInterceptor implements HandlerInterceptor {
@Autowired
private TokenHandle tokenHandle;
@Value("${server.needAuthentication:true}")
protected boolean needAuthentication;
/**
* 请求处理前执行
* 验证用户登录状态,保存登录信息到线程本地变量
*/
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
//如果是测试环境,不走拦截器
if (!needAuthentication) {
return true;
}
// 获取登录用户信息
LoginUser loginUser = tokenHandle.getLoginUser(request);
......
package com.jomalls.custom.config;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import org.jspecify.annotations.NonNull;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class ThreadFactoryHandle implements java.util.concurrent.ThreadFactory {
private final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadFactory defaultThreadFactory = java.util.concurrent.Executors.defaultThreadFactory();
@Slf4j
public class ThreadFactoryHandle implements ThreadFactory {
private final ThreadGroup group;
private final String namePrefix;
private static final AtomicInteger THREAD_NUMBER = new AtomicInteger(1);
private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = defaultThreadFactory.newThread(r);
thread.setName("custom-server-" + poolNumber.getAndIncrement() + "-thread");
return thread;
public Thread newThread(@NonNull Runnable r) {
Thread t = new Thread(group, r,namePrefix + THREAD_NUMBER.getAndIncrement(),0);
log.debug("线程池创建的线程 --- :{}", t.threadId());
if (t.isDaemon()){
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY){
t.setPriority(Thread.NORM_PRIORITY);
}
t.setUncaughtExceptionHandler((thread,exception)->{
try {
throw exception;
} catch (Throwable throwable) {
log.error("线程池运行异常:{}",throwable.getMessage(), throwable);
}
});
return t;
}
ThreadFactoryHandle() {
group = Thread.currentThread().getThreadGroup();
namePrefix = "custom-server-" + POOL_NUMBER.getAndIncrement() + "-thread-";
}
}
\ No newline at end of file
package com.jomalls.custom.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class ThreadPoolAutoConfiguration {
@Bean("customServerThreadPool")
public ThreadPoolExecutor labelCenterThreadPoolExecutor() {
return new ThreadPoolExecutorConfig(Runtime.getRuntime().availableProcessors() + 1,
Runtime.getRuntime().availableProcessors()*2,
60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3000),
new ThreadFactoryHandle());
}
}
\ No newline at end of file
package com.jomalls.custom.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.jomalls.custom.app.utils.RequestHolder;
import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.NonNull;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.concurrent.Executor;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Configuration
public class ThreadPoolExecutorConfig {
public class ThreadPoolExecutorConfig extends ThreadPoolExecutor {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("custom-server-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
public ThreadPoolExecutorConfig(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
@Override
public void execute(@NonNull Runnable command) {
//设置子线程共享
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
RequestContextHolder.setRequestAttributes(attributes, true);
}
HttpServletRequest request = RequestHolder.getRequestHolder();
super.execute(() -> {
if(null != request){
RequestHolder.setRequestHolder(request);
}
try {
command.run();
} catch (Exception e) {
throw new RuntimeException("异步线程执行失败", e);
}
finally {
RequestHolder.removeRequestHolder();
}
});
}
}
\ No newline at end of file
......@@ -22,6 +22,11 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
"/actuator/health", "/health/check");
}
/**
* 配置服务器跨域请求被允许
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
......
......@@ -33,6 +33,7 @@ default.scp.data.version=1.0
## 时区配置
TZ=Asia/Shanghai
server.needAuthentication=false
# 令牌自定义标识
token.header=Authorization
# 令牌密钥
......
......@@ -20,6 +20,7 @@
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<log4j.version>2.23.1</log4j.version>
<fastjson2.version>2.0.54</fastjson2.version>
<mybatis-plus.version>3.5.8</mybatis-plus.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
......@@ -97,6 +98,12 @@
<artifactId>mybatis-plus-extension</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment