9 changed files with 255 additions and 1 deletions
After Width: | Height: | Size: 75 KiB |
@ -0,0 +1,30 @@ |
|||||||
|
package top.naccl.dwz.annotation; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description: 访问控制 |
||||||
|
* @Author: Naccl |
||||||
|
* @Date: 2021-09-16 |
||||||
|
*/ |
||||||
|
@Target(ElementType.METHOD) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface AccessLimit { |
||||||
|
/** |
||||||
|
* 限制周期(秒) |
||||||
|
*/ |
||||||
|
int seconds(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 规定周期内限制次数 |
||||||
|
*/ |
||||||
|
int maxCount(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 触发限制时的消息提示 |
||||||
|
*/ |
||||||
|
String msg() default "操作频率过高"; |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package top.naccl.dwz.config; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||||
|
import top.naccl.dwz.interceptor.AccessLimitInterceptor; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description: 配置CORS跨域支持、拦截器 |
||||||
|
* @Author: Naccl |
||||||
|
* @Date: 2020-09-16 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class WebConfig implements WebMvcConfigurer { |
||||||
|
@Autowired |
||||||
|
AccessLimitInterceptor accessLimitInterceptor; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addInterceptors(InterceptorRegistry registry) { |
||||||
|
registry.addInterceptor(accessLimitInterceptor); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package top.naccl.dwz.interceptor; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.web.method.HandlerMethod; |
||||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||||
|
import top.naccl.dwz.annotation.AccessLimit; |
||||||
|
import top.naccl.dwz.entity.R; |
||||||
|
import top.naccl.dwz.util.IpAddressUtils; |
||||||
|
import top.naccl.dwz.util.JacksonUtils; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description: 访问控制拦截器 |
||||||
|
* @Author: Naccl |
||||||
|
* @Date: 2021-09-16 |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class AccessLimitInterceptor implements HandlerInterceptor { |
||||||
|
@Autowired |
||||||
|
StringRedisTemplate redisTemplate; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
||||||
|
if (handler instanceof HandlerMethod) { |
||||||
|
HandlerMethod handlerMethod = (HandlerMethod) handler; |
||||||
|
AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class); |
||||||
|
//方法上没有访问控制的注解,直接通过
|
||||||
|
if (accessLimit == null) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
int seconds = accessLimit.seconds(); |
||||||
|
int maxCount = accessLimit.maxCount(); |
||||||
|
String ip = IpAddressUtils.getIpAddress(request); |
||||||
|
String method = request.getMethod(); |
||||||
|
String requestURI = request.getRequestURI(); |
||||||
|
|
||||||
|
String redisKey = ip + ":" + method + ":" + requestURI; |
||||||
|
Object redisResult = redisTemplate.opsForValue().get(redisKey); |
||||||
|
Integer count = JacksonUtils.convertValue(redisResult, Integer.class); |
||||||
|
if (count == null) { |
||||||
|
//在规定周期内第一次访问,存入redis
|
||||||
|
redisTemplate.opsForValue().increment(redisKey, 1); |
||||||
|
redisTemplate.expire(redisKey, seconds, TimeUnit.SECONDS); |
||||||
|
} else { |
||||||
|
if (count >= maxCount) { |
||||||
|
//超出访问限制次数
|
||||||
|
response.setContentType("application/json;charset=utf-8"); |
||||||
|
PrintWriter out = response.getWriter(); |
||||||
|
R result = R.create(403, accessLimit.msg()); |
||||||
|
out.write(JacksonUtils.writeValueAsString(result)); |
||||||
|
out.flush(); |
||||||
|
out.close(); |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
//没超出访问限制次数
|
||||||
|
redisTemplate.opsForValue().increment(redisKey, 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package top.naccl.dwz.util; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.UnknownHostException; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description: ip记录 |
||||||
|
* @Author: Naccl |
||||||
|
* @Date: 2020-08-18 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class IpAddressUtils { |
||||||
|
/** |
||||||
|
* 在Nginx等代理之后获取用户真实IP地址 |
||||||
|
* |
||||||
|
* @param request |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getIpAddress(HttpServletRequest request) { |
||||||
|
String ip = request.getHeader("x-forwarded-for"); |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("Proxy-Client-IP"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR"); |
||||||
|
} |
||||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||||
|
ip = request.getRemoteAddr(); |
||||||
|
if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) { |
||||||
|
//根据网卡取本机配置的IP
|
||||||
|
InetAddress inet = null; |
||||||
|
try { |
||||||
|
inet = InetAddress.getLocalHost(); |
||||||
|
} catch (UnknownHostException e) { |
||||||
|
log.error("getIpAddress exception:", e); |
||||||
|
} |
||||||
|
ip = inet.getHostAddress(); |
||||||
|
} |
||||||
|
} |
||||||
|
return StringUtils.substringBefore(ip, ","); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
package top.naccl.dwz.util; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
/** |
||||||
|
* @Description: Jackson Object Mapper |
||||||
|
* @Author: Naccl |
||||||
|
* @Date: 2020-11-07 |
||||||
|
*/ |
||||||
|
public class JacksonUtils { |
||||||
|
private static ObjectMapper objectMapper = new ObjectMapper(); |
||||||
|
|
||||||
|
public static String writeValueAsString(Object value) { |
||||||
|
try { |
||||||
|
return objectMapper.writeValueAsString(value); |
||||||
|
} catch (JsonProcessingException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> T readValue(String content, Class<T> valueType) { |
||||||
|
try { |
||||||
|
return objectMapper.readValue(content, valueType); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> T readValue(String content, TypeReference<T> valueTypeRef) { |
||||||
|
try { |
||||||
|
return objectMapper.readValue(content, valueTypeRef); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> T readValue(InputStream src, Class<T> valueType) { |
||||||
|
try { |
||||||
|
return objectMapper.readValue(src, valueType); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static <T> T convertValue(Object fromValue, Class<T> toValueType) { |
||||||
|
return objectMapper.convertValue(fromValue, toValueType); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue