修复了一些已知的安全问题

1. 🔒 安全问题处理
2.  我的钱包接口
This commit is contained in:
ronger 2021-12-12 10:47:36 +08:00 committed by GitHub
commit 547f11abe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 173 additions and 8 deletions

View File

@ -7,6 +7,7 @@ import com.rymcu.forest.core.result.GlobalResult;
import com.rymcu.forest.core.result.ResultCode;
import com.rymcu.forest.enumerate.TransactionCode;
import com.rymcu.forest.web.api.exception.BaseApiException;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
@ -39,9 +40,9 @@ public class BaseExceptionHandler {
if (isAjax(request)) {
GlobalResult result = new GlobalResult();
if (ex instanceof BaseApiException) {
result.setCode(401);
result.setMessage("用户未登录");
logger.info("用户未登录");
result.setCode(((BaseApiException) ex).getCode());
result.setMessage(((BaseApiException) ex).getExtraMessage());
logger.info(result.getMessage());
} else if (ex instanceof UnauthenticatedException) {
result.setCode(1000001);
result.setMessage("token错误");
@ -88,8 +89,8 @@ public class BaseExceptionHandler {
FastJsonJsonView view = new FastJsonJsonView();
Map<String, Object> attributes = new HashMap(2);
if (ex instanceof BaseApiException) {
attributes.put("code", "401");
attributes.put("message", "用户未登录");
attributes.put("code", ((BaseApiException) ex).getCode());
attributes.put("message", ((BaseApiException) ex).getExtraMessage());
} else if (ex instanceof UnauthenticatedException) {
attributes.put("code", "1000001");
attributes.put("message", "token错误");
@ -138,8 +139,8 @@ public class BaseExceptionHandler {
String requestedWith = request.getHeader("x-requested-with");
if (requestedWith != null && "XMLHttpRequest".equalsIgnoreCase(requestedWith)) {
return true;
} else {
return false;
}
String contentType = request.getContentType();
return StringUtils.isNotBlank(contentType) && contentType.contains("application/json");
}
}

View File

@ -0,0 +1,109 @@
package com.rymcu.forest.core.service.security;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rymcu.forest.dto.TokenUser;
import com.rymcu.forest.jwt.def.JwtConstants;
import com.rymcu.forest.util.UserUtils;
import com.rymcu.forest.web.api.exception.BaseApiException;
import com.rymcu.forest.web.api.exception.ErrorCode;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 检查用户修改信息权限
*
* @author ronger
*/
@Aspect
@Component
public class SecurityAspect {
Logger logger = LoggerFactory.getLogger(SecurityAspect.class);
@Pointcut("@annotation(com.rymcu.forest.core.service.security.annotation.SecurityInterceptor)")
public void pointCut() {
}
/**
* 检查用户修改信息权限
*
* @param joinPoint 连接点
* @return 方法执行结果
* @throws Throwable 调用出错
*/
@Before(value = "pointCut()")
public void doBefore(JoinPoint joinPoint) throws BaseApiException {
logger.info("检查用户修改信息权限 start ...");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String idUser = "";
if (isAjax(request)) {
Object[] objects = joinPoint.getArgs();
JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(objects[0]));
if (Objects.nonNull(jsonObject)) {
idUser = jsonObject.getString("idUser");
}
} else {
Map params = getParams(request);
if (params.isEmpty()) {
params = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
} else {
params.putAll((Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE));
}
idUser = (String) params.get("idUser");
}
if (Objects.nonNull(idUser)) {
String authHeader = request.getHeader(JwtConstants.AUTHORIZATION);
if (StringUtils.isNotBlank(authHeader)) {
TokenUser tokenUser = UserUtils.getTokenUser(authHeader);
if (Objects.nonNull(tokenUser)) {
if (!idUser.equals(tokenUser.getIdUser().toString())) {
throw new BaseApiException(ErrorCode.ACCESS_DENIED);
}
} else {
throw new BaseApiException(ErrorCode.ACCESS_DENIED);
}
}
} else {
throw new BaseApiException(ErrorCode.ACCESS_DENIED);
}
logger.info("检查用户修改信息权限 end ...");
}
private Map<String, String> getParams(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(10);
Enumeration<String> paraNames = request.getParameterNames();
while (paraNames.hasMoreElements()) {
String key = paraNames.nextElement();
if ("password".equals(key)) {
continue;
}
paramsMap.put(key, request.getParameter(key));
}
return paramsMap;
}
private boolean isAjax(HttpServletRequest request) {
String requestedWith = request.getHeader("x-requested-with");
if (requestedWith != null && "XMLHttpRequest".equalsIgnoreCase(requestedWith)) {
return true;
}
String contentType = request.getContentType();
return StringUtils.isNotBlank(contentType) && contentType.contains("application/json");
}
}

View File

@ -0,0 +1,12 @@
package com.rymcu.forest.core.service.security.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 安全拦截器
* @author ronger
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityInterceptor {
}

View File

@ -66,10 +66,10 @@ public class RestAuthTokenInterceptor implements HandlerInterceptor {
request.setAttribute(JwtConstants.CURRENT_TOKEN_CLAIMS, claims);
//如果token验证成功将token对应的用户id存在request中便于之后注入
request.setAttribute(JwtConstants.CURRENT_USER_NAME, model.getUsername());
String uri = request.getRequestURI();
// 判断是否为后台接口或财政划转接口
String adminApi = "/admin";
String transactionApi = "/transaction";
String uri = request.getRequestURI();
if (uri.contains(adminApi) || uri.contains(transactionApi)) {
// 判断管理员权限
boolean hasPermission = userMapper.hasAdminPermission(model.getUsername());

View File

@ -0,0 +1,35 @@
package com.rymcu.forest.web.api.bank;
import com.rymcu.forest.core.result.GlobalResult;
import com.rymcu.forest.core.result.GlobalResultGenerator;
import com.rymcu.forest.core.service.security.annotation.SecurityInterceptor;
import com.rymcu.forest.dto.BankAccountDTO;
import com.rymcu.forest.service.BankAccountService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Created on 2021/12/10 19:25.
*
* @author ronger
* @email ronger-x@outlook.com
*/
@RestController
@RequestMapping("/api/v1/wallet")
public class WalletController {
@Resource
private BankAccountService bankAccountService;
@GetMapping("/{idUser}")
@SecurityInterceptor
public GlobalResult detail(@PathVariable Integer idUser) {
BankAccountDTO bankAccount = bankAccountService.findBankAccountByIdUser(idUser);
return GlobalResultGenerator.genSuccessResult(bankAccount);
}
}

View File

@ -2,6 +2,7 @@ package com.rymcu.forest.web.api.exception;
/**
* 服务业务异常如 账号或密码错误 该异常只做INFO级别的日志记录 @see WebMvcConfigurer
* @author ronger
*/
public class BaseApiException extends Exception {

View File

@ -2,6 +2,7 @@ package com.rymcu.forest.web.api.user;
import com.rymcu.forest.core.result.GlobalResult;
import com.rymcu.forest.core.result.GlobalResultGenerator;
import com.rymcu.forest.core.service.security.annotation.SecurityInterceptor;
import com.rymcu.forest.dto.*;
import com.rymcu.forest.entity.UserExtend;
import com.rymcu.forest.service.UserService;
@ -21,36 +22,42 @@ public class UserInfoController {
private UserService userService;
@GetMapping("/detail/{idUser}")
@SecurityInterceptor
public GlobalResult detail(@PathVariable Integer idUser) {
Map map = userService.findUserInfo(idUser);
return GlobalResultGenerator.genSuccessResult(map);
}
@GetMapping("/check-nickname")
@SecurityInterceptor
public GlobalResult checkNickname(@RequestParam Integer idUser, @RequestParam String nickname) {
Map map = userService.checkNickname(idUser,nickname);
return GlobalResultGenerator.genSuccessResult(map);
}
@PatchMapping("/update")
@SecurityInterceptor
public GlobalResult updateUserInfo(@RequestBody UserInfoDTO user) {
Map map = userService.updateUserInfo(user);
return GlobalResultGenerator.genSuccessResult(map);
}
@PatchMapping("/update-extend")
@SecurityInterceptor
public GlobalResult updateUserExtend(@RequestBody UserExtend userExtend) {
Map map = userService.updateUserExtend(userExtend);
return GlobalResultGenerator.genSuccessResult(map);
}
@PatchMapping("/update-email")
@SecurityInterceptor
public GlobalResult updateEmail(@RequestBody ChangeEmailDTO changeEmailDTO) {
Map map = userService.updateEmail(changeEmailDTO);
return GlobalResultGenerator.genSuccessResult(map);
}
@PatchMapping("/update-password")
@SecurityInterceptor
public GlobalResult updatePassword(@RequestBody UpdatePasswordDTO updatePasswordDTO) {
Map map = userService.updatePassword(updatePasswordDTO);
return GlobalResultGenerator.genSuccessResult(map);