package com.nq.common.interceptor; import com.alibaba.fastjson.JSON; import com.google.gson.Gson; import com.nq.annotation.SameUrlData; import com.nq.common.ServerResponse; import com.nq.pojo.User; import com.nq.utils.PropertiesUtil; import com.nq.utils.redis.JsonUtil; import com.nq.utils.redis.RedisShardedPoolUtils; import com.nq.utils.translate.GoogleTranslateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpMethod; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; @Component public class ApiUserAuthorityInterceptor implements HandlerInterceptor { private static final Logger log = LoggerFactory.getLogger(ApiUserAuthorityInterceptor.class); private static final Set PAY_CALLBACK_ALLOW_IPS = new HashSet<>( Arrays.asList("3.111.236.70", "13.233.3.123") ); private RedisTemplate redisTemplate; public ApiUserAuthorityInterceptor(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { // httpServletResponse.setHeader("Access-Control-Allow-Origin",httpServletRequest.getHeader("origin")); // //该字段可选,是个布尔值,表示是否可以携带cookie // httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true"); // httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT,PATCH, DELETE, OPTIONS"); // httpServletResponse.setHeader("Access-Control-Allow-Headers", "*"); // httpServletResponse.setHeader("Access-Control-Allow-Headers", "usertoken"); if (HttpMethod.OPTIONS.toString().equals(httpServletRequest.getMethod())){ return true; } String url = httpServletRequest.getRequestURI(); if (isPayCallbackUrl(url) && !isAllowedPayCallbackIp(httpServletRequest)) { log.warn("拦截非白名单回调IP, url={}, ip={}", url, extractClientIp(httpServletRequest)); httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); return false; } if ("/user/upload.do".equals(url)) { return true; } if ("/user/pay.do".equals(url)) { return true; } if ("/user/list.do".equals(url)) { return true; } if ("/user/newStockBuy.do".equals(url)) { return true; } if ("/user/buchahbds.do".equals(url)) { return true; } if ("/user/pay/flyPay.do".equals(url)) { return true; } if ("/user/rechargeCallback.do".equals(url)) {//支付回调 return true; } if ("/user/rechargeCallbackTwo.do".equals(url)) {//支付回调 return true; } if ("/user/rechargeCallbackZero.do".equals(url)) {//支付回调 return true; } if ("/user/rechargeCallbackThree.do".equals(url)) {//支付回调 return true; } if ("/user/rechargeCallbackFour.do".equals(url)) {//支付4回调 return true; } if ("/user/payoutCallback.do".equals(url)) {//代付回调 return true; } if ("/user/payoutCallbackTwo.do".equals(url)) {//代付回调 return true; } if ("/user/payoutCallbackThree.do".equals(url)) {//代付v2回调 return true; } User currentUser = getCurrentUser(httpServletRequest); GoogleTranslateUtil googleTranslateUtil = new GoogleTranslateUtil(); String lang = httpServletRequest.getHeader("lang"); if (null == currentUser) { httpServletResponse.setCharacterEncoding("UTF-8"); httpServletResponse.setContentType("application/json;charset=UTF-8"); PrintWriter writer = httpServletResponse.getWriter(); writer.print( new Gson().toJson(ServerResponse.createByErrorCodeMsg(401,googleTranslateUtil.translate("请登录",lang )))); writer.flush(); writer.close(); return false; } String uri = httpServletRequest.getRequestURI(); //验证重复点击与接口权限等 Boolean checkFlag = checkUri(httpServletResponse, handler, uri,currentUser); if (!checkFlag) { return Boolean.FALSE; } //判断请求头 return true; } private boolean isPayCallbackUrl(String url) { return "/user/rechargeCallbackFour.do".equals(url) || "/user/payoutCallbackThree.do".equals(url); } private boolean isAllowedPayCallbackIp(HttpServletRequest request) { String ip = extractClientIp(request); return PAY_CALLBACK_ALLOW_IPS.contains(ip); } private String extractClientIp(HttpServletRequest request) { String forwarded = request.getHeader("X-Forwarded-For"); if (forwarded != null && !forwarded.trim().isEmpty()) { String first = forwarded.split(",")[0].trim(); if (!first.isEmpty()) { return first; } } String realIp = request.getHeader("X-Real-IP"); if (realIp != null && !realIp.trim().isEmpty()) { return realIp.trim(); } return request.getRemoteAddr(); } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView) throws Exception { } /** * token验证 * * @param response 返回 * @param handler 请求头 * @return R 的统一返回,并拦截 * @throws Exception 异常情况,则为服务异常 */ private Boolean checkUri(HttpServletResponse response, Object handler, String uri,User user) throws Exception { //判断连续请求的情况 if (handler instanceof HandlerMethod) { SameUrlData tokenTypeAnnotation = findAnnotation((HandlerMethod) handler, SameUrlData.class); //判断是否需要验证连续点击 if (tokenTypeAnnotation != null && redisTemplate.hasKey(user.getId() + uri)) { return responseWrite(response, "請勿頻繁點擊"); } else { //将接口+用户信息存储到缓存中。进行重复点击校验 redisTemplate.opsForValue().set(user.getId()+ uri, "", 30, TimeUnit.SECONDS); } } return true; } private Boolean responseWrite(HttpServletResponse response, String resultMSG) throws Exception { //throw new BaseException(resultCode); // 定义返回类型为JSON response.setContentType("application/json;charset=UTF-8"); // 获取PrintWriter PrintWriter out = response.getWriter(); //将异常类型写入 ServerResponse byErrorMsg = ServerResponse.createByErrorMsg(resultMSG); out.write(JSON.toJSONString(byErrorMsg)); //输出流 out.flush(); //关闭请求 out.close(); return true; } /** * 请求结束后进行的操作 * * @param request 请求信息 * @param response 返回信息 * @param handler 请求头 * @param ex 异常情况 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) { String uri = request.getRequestURI(); if (handler instanceof HandlerMethod) { User currentUser = getCurrentUser(request); SameUrlData tokenTypeAnnotation = findAnnotation((HandlerMethod) handler, SameUrlData.class); //没有声明需要权限,或者声明不验证权限 if (tokenTypeAnnotation != null) { redisTemplate.delete(currentUser.getId() + uri); } } } /** * 获取接口上的注解 * * @param handler 请求方法 * @param annotationType 想要获取的注解 * @param 想要获取的注解类型 * @return 注解 */ private T findAnnotation(HandlerMethod handler, Class annotationType) { T annotation = handler.getBeanType().getAnnotation(annotationType); if (annotation != null) { return annotation; } return handler.getMethodAnnotation(annotationType); } /** * 当前用户token鉴权 * * @param request 请求 * @return 当前用户 */ public User getCurrentUser(HttpServletRequest request) { String property = PropertiesUtil.getProperty("user.cookie.name"); String loginToken = request.getHeader(property); if (loginToken == null) { return null; } String userJson = RedisShardedPoolUtils.get(loginToken); if (userJson == null||"".equals(userJson)){ return null; } return (User) JsonUtil.string2Obj(userJson, User.class); } }