refactor: 添加 websocket 连接认证拦截器实现点对点指定用户发送消息;移除 easy-captcha 替换为 hutool-captcha验证码实现代码简化;重构认证接口控制层代码。

This commit is contained in:
haoxr
2023-09-12 18:25:16 +08:00
parent 87fcf022ba
commit 9453600715
37 changed files with 290 additions and 358 deletions

View File

@@ -4,7 +4,7 @@ package com.youlai.system.common.constant;
* Security 常量
*
* @author haoxr
* @since 3.0.0
* @since 2.0.0
*/
public interface SecurityConstants {

View File

@@ -1,35 +0,0 @@
package com.youlai.system.common.exception;
import com.youlai.system.common.result.IResultCode;
import lombok.Getter;
/**
* 自定义业务异常
*
* @author haoxr
* @since 2022/7/31
*/
@Getter
public class BusinessException extends RuntimeException {
public IResultCode resultCode;
public BusinessException(IResultCode errorCode) {
super(errorCode.getMsg());
this.resultCode = errorCode;
}
public BusinessException(String message){
super(message);
}
public BusinessException(String message, Throwable cause){
super(message, cause);
}
public BusinessException(Throwable cause){
super(cause);
}
}

View File

@@ -1,211 +0,0 @@
package com.youlai.system.common.exception;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.youlai.system.common.result.Result;
import com.youlai.system.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import jakarta.servlet.ServletException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.sql.SQLSyntaxErrorException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 全局系统异常处理器
* <p>
* 调整异常处理的HTTP状态码丰富异常处理类型
*
* @author Gadfly
* @since 2020-02-25 13:54
**/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(BindException e) {
log.error("BindException:{}", e.getMessage());
String msg = e.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(""));
return Result.failed(ResultCode.PARAM_ERROR, msg);
}
/**
* RequestParam参数的校验
*
* @param e
* @param <T>
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(ConstraintViolationException e) {
log.error("ConstraintViolationException:{}", e.getMessage());
String msg = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(""));
return Result.failed(ResultCode.PARAM_ERROR, msg);
}
/**
* RequestBody参数的校验
*
* @param e
* @param <T>
* @return
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(MethodArgumentNotValidException e) {
log.error("MethodArgumentNotValidException:{}", e.getMessage());
String msg = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(""));
return Result.failed(ResultCode.PARAM_ERROR, msg);
}
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public <T> Result<T> processException(NoHandlerFoundException e) {
log.error(e.getMessage(), e);
return Result.failed(ResultCode.RESOURCE_NOT_FOUND);
}
/**
* MissingServletRequestParameterException
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(MissingServletRequestParameterException e) {
log.error(e.getMessage(), e);
return Result.failed(ResultCode.PARAM_IS_NULL);
}
/**
* MethodArgumentTypeMismatchException
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(MethodArgumentTypeMismatchException e) {
log.error(e.getMessage(), e);
return Result.failed(ResultCode.PARAM_ERROR, "类型错误");
}
/**
* ServletException
*/
@ExceptionHandler(ServletException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(ServletException e) {
log.error(e.getMessage(), e);
return Result.failed(e.getMessage());
}
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> handleIllegalArgumentException(IllegalArgumentException e) {
log.error("非法参数异常,异常原因:{}", e.getMessage(), e);
return Result.failed(e.getMessage());
}
@ExceptionHandler(JsonProcessingException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> handleJsonProcessingException(JsonProcessingException e) {
log.error("Json转换异常异常原因{}", e.getMessage(), e);
return Result.failed(e.getMessage());
}
/**
* HttpMessageNotReadableException
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(HttpMessageNotReadableException e) {
log.error(e.getMessage(), e);
String errorMessage = "请求体不可为空";
Throwable cause = e.getCause();
if (cause != null) {
errorMessage = convertMessage(cause);
}
return Result.failed(errorMessage);
}
@ExceptionHandler(TypeMismatchException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> processException(TypeMismatchException e) {
log.error(e.getMessage(), e);
return Result.failed(e.getMessage());
}
@ExceptionHandler(BadSqlGrammarException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public <T> Result<T> handleBadSqlGrammarException(BadSqlGrammarException e) {
log.error(e.getMessage(), e);
String errorMsg = e.getMessage();
if (StrUtil.isNotBlank(errorMsg) && errorMsg.contains("denied to user")) {
return Result.failed(ResultCode.FORBIDDEN_OPERATION);
} else {
return Result.failed(e.getMessage());
}
}
@ExceptionHandler(SQLSyntaxErrorException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public <T> Result<T> processSQLSyntaxErrorException(SQLSyntaxErrorException e) {
log.error(e.getMessage(), e);
return Result.failed(e.getMessage());
}
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> handleBizException(BusinessException e) {
log.error("biz exception: {}", e.getMessage());
if (e.getResultCode() != null) {
return Result.failed(e.getResultCode());
}
return Result.failed(e.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public <T> Result<T> handleException(Exception e) {
log.error("unknown exception: {}", e.getMessage());
return Result.failed(e.getLocalizedMessage());
}
/**
* 传参类型错误时,用于消息转换
*
* @param throwable 异常
* @return 错误信息
*/
private String convertMessage(Throwable throwable) {
String error = throwable.toString();
String regulation = "\\[\"(.*?)\"]+";
Pattern pattern = Pattern.compile(regulation);
Matcher matcher = pattern.matcher(error);
String group = "";
if (matcher.find()) {
String matchString = matcher.group();
matchString = matchString.replace("[", "").replace("]", "");
matchString = "%s字段类型错误".formatted(matchString.replaceAll("\\\"", ""));
group += matchString;
}
return group;
}
}

View File

@@ -1,20 +0,0 @@
package com.youlai.system.common.util;
import com.alibaba.excel.EasyExcel;
import com.youlai.system.listener.easyexcel.MyAnalysisEventListener;
import java.io.InputStream;
/**
* Excel 工具类
*
* @author haoxr
* @since 2023/03/01
*/
public class ExcelUtils {
public static <T> String importExcel(InputStream is, Class clazz, MyAnalysisEventListener<T> listener) {
EasyExcel.read(is, clazz, listener).sheet().doRead();
return listener.getMsg();
}
}

View File

@@ -1,24 +0,0 @@
package com.youlai.system.common.util;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants;
import jakarta.servlet.http.HttpServletRequest;
/**
* 请求工具类
*
* @author haoxr
*/
public class RequestUtils {
/**
* 请求头解析获取 Token
*/
public static String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(SecurityConstants.TOKEN_KEY);
if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith(SecurityConstants.TOKEN_PREFIX)) {
return bearerToken.substring(SecurityConstants.TOKEN_PREFIX.length());
}
return null;
}
}

View File

@@ -1,46 +0,0 @@
package com.youlai.system.common.util;
import cn.hutool.json.JSONUtil;
import com.youlai.system.common.result.Result;
import com.youlai.system.common.result.ResultCode;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 响应工具类
*
* @author haoxr
* @since 2022/10/18
*/
public class ResponseUtils {
/**
* 异常消息返回(适用过滤器中处理异常响应)
*
* @param response
* @param resultCode
*/
public static void writeErrMsg(HttpServletResponse response, ResultCode resultCode) throws IOException {
switch (resultCode) {
case ACCESS_UNAUTHORIZED:
case TOKEN_INVALID:
response.setStatus(HttpStatus.UNAUTHORIZED.value());
break;
case TOKEN_ACCESS_FORBIDDEN:
response.setStatus(HttpStatus.FORBIDDEN.value());
break;
default:
response.setStatus(HttpStatus.BAD_REQUEST.value());
break;
}
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.getWriter().print(JSONUtil.toJsonStr(Result.failed(resultCode)));
}
}

View File

@@ -1,141 +0,0 @@
package com.youlai.system.common.util;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SystemConstants;
import com.youlai.system.security.userdetails.SysUserDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.PatternMatchUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
public class SecurityUtils {
/**
* 获取当前登录人信息
*
* @return
*/
public static SysUserDetails getUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Object principal = authentication.getPrincipal();
if (principal instanceof SysUserDetails) {
return (SysUserDetails) authentication.getPrincipal();
}
}
return null;
}
/**
* 获取用户ID
*
* @return
*/
public static Long getUserId() {
Long userId = Convert.toLong(getUser().getUserId());
return userId;
}
/**
* 获取部门ID
*
* @return
*/
public static Long getDeptId() {
Long userId = Convert.toLong(getUser().getDeptId());
return userId;
}
/**
* 获取数据权限范围
*
* @return DataScope
*/
public static Integer getDataScope() {
Integer dataScope = Convert.toInt(getUser().getDataScope());
return dataScope;
}
/**
* 获取用户角色集合
*
* @return
*/
public static Set<String> getRoles() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
if (CollectionUtil.isNotEmpty(authorities)) {
Set<String> roles = authorities.stream().filter(item -> item.getAuthority().startsWith("ROLE_"))
.map(item -> StrUtil.removePrefix(item.getAuthority(), "ROLE_"))
.collect(Collectors.toSet());
return roles;
}
}
return Collections.EMPTY_SET;
}
/**
* 获取用户权限集合
*
* @return
*/
public static Set<String> getPerms() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
if (CollectionUtil.isNotEmpty(authorities)) {
Set<String> perms = authorities.stream().filter(item -> !item.getAuthority().startsWith("ROLE_"))
.map(item -> item.getAuthority())
.collect(Collectors.toSet());
return perms;
}
}
return Collections.EMPTY_SET;
}
/**
* 是否超级管理员
* <p>
* 超级管理员忽视任何权限判断
*
* @return
*/
public static boolean isRoot() {
Set<String> roles = getRoles();
if (roles.contains(SystemConstants.ROOT_ROLE_CODE)) {
return true;
}
return false;
}
/**
* 是否拥有权限判断
* <p>
* 适用业务判断(接口权限判断适用Spring Security 自带注解 PreAuthorize 判断即可 )
*
* @return
*/
public static boolean hasPerm(String perm) {
if (isRoot()) {
return true;
}
Set<String> perms = getPerms();
boolean hasPerm = perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
return hasPerm;
}
}