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