SpringBoot利用Redis实现接口限流

依赖

        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        

配置

即redis相关配置

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
    username: root
    password: abc[mYedmy7Bank+r28xgfer+wxEgIsCY+lnHTmZlzzwdQ1R0jFXBRVPUc6UUKAQm3mN]
  redis:
    host: localhost
    port: 6379
    password:
    database: 15
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 500
        min-idle: 0
    lettuce:
      shutdown-timeout: 0

自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义一个注解标明需要使用限流的接口
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {

    int seconds();

    int maxCount();

}

拦截器配置

1、拦截器WebSecurityConfig配置

import com.alibaba.fastjson.JSONObject;
import com.one.smile.test.service.RedisService;
import com.one.smile.test.utils.AccessLimit;
import com.one.smile.test.utils.SystemUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;

@Configuration
public class WebSecurityConfig implements HandlerInterceptor {

    @Autowired
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果请求输入方法
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            //获取方法中的注解,看是否有该注解
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if (accessLimit != null) {
                long seconds = accessLimit.seconds();
                int maxCount = accessLimit.maxCount();
                //关于key的生成规则可以自己定义。本项目需求是对每个方法都加上限流功能,如果你只是针对ip地址做限流,那么key只需要用ip即可
                String key = SystemUtil.getIpAddr(request) + hm.getMethod().getName();
                //从redis中获取用户访问的次数
                //此操作代表取该key对应的值自增1后的结果
                long q = redisService.incr(key, seconds);
                if (q > maxCount) {
                    //加1
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("status", 0);
                    jsonObject.put("msg", "请求过于频繁,请稍后再试");
                    jsonObject.put("data", null);
                    render(response, jsonObject);
                    return false;
                }
                return true;
            }
        }
        return true;
    }

    private void render(HttpServletResponse response, JSONObject cm) throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str = cm.toString();
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }

}

2、拦截器中的RedisService

public interface RedisService {

    /**
     * set存数据
     *
     * @param key
     * @param value
     * @return
     */
    boolean set(String key, String value);

    /**
     * get获取数据
     *
     * @param key
     * @return
     */
    String get(String key);

    /**
     * 设置有效天数
     *
     * @param key
     * @param expire
     * @return
     */
    boolean expire(String key, long expire);

    /**
     * 移除数据
     *
     * @param key
     * @return
     */
    boolean remove(String key);

    /**
     * 获取自增1后的 值
     *
     * @param key
     * @param time
     * @return
     */
    Long incr(String key, long time);

}

3、service对应的实现类

import com.one.smile.test.service.RedisService;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class RedisServiceImpl implements RedisService {

    @Resource
    private RedisTemplate redisTemplate;


    @Override
    public boolean set(final String key, final String value) {
        boolean result = redisTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer serializer = redisTemplate.getStringSerializer();
                connection.set(serializer.serialize(key), serializer.serialize(value));
                return true;
            }
        });
        return result;
    }

    @Override
    public String get(final String key) {
        String result = redisTemplate.execute(new RedisCallback() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer serializer = redisTemplate.getStringSerializer();
                byte[] value = connection.get(serializer.serialize(key));
                return serializer.deserialize(value);
            }
        });
        return result;
    }

    @Override
    public boolean expire(final String key, long expire) {
        return redisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    @Override
    public boolean remove(final String key) {
        boolean result = redisTemplate.execute(new RedisCallback() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer serializer = redisTemplate.getStringSerializer();
                connection.del(key.getBytes());
                return true;
            }
        });
        return result;
    }

    @Override
    public Long incr(String key, long time) {
        long count = redisTemplate.opsForValue().increment(key, 1);
        if (count == 1) {
            //设置有效期一分钟
            set(key, "1");
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
        return count;
    }

}

4、拦截器中的SystemUtil

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;

/**
 * 获取系统的信息
 */
public final class SystemUtil {

    /**
     * 命令获取mac地址
     *
     * @param cmd
     * @return
     */
    private static String callCmd(String[] cmd) {
        String result = "";
        String line = "";
        try {
            Process proc = Runtime.getRuntime().exec(cmd);
            InputStreamReader is = new InputStreamReader(proc.getInputStream());
            BufferedReader br = new BufferedReader(is);
            while ((line = br.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @param cmd     第一个命令
     * @param another 第二个命令
     * @return 第二个命令的执行结果
     */

    private static String callCmd(String[] cmd, String[] another) {
        String result = "";
        String line = "";
        try {
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
            proc.waitFor(); // 已经执行完第一个命令,准备执行第二个命令
            proc = rt.exec(another);
            InputStreamReader is = new InputStreamReader(proc.getInputStream());
            BufferedReader br = new BufferedReader(is);
            while ((line = br.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @param ip           目标ip,一般在局域网内
     * @param sourceString 命令处理的结果字符串
     * @param macSeparator mac分隔符号
     * @return mac地址,用上面的分隔符号表示
     */

    private static String filterMacAddress(final String ip, final String sourceString, final String macSeparator) {
        String result = "";
        String regExp = "((([0-9,A-F,a-f]{1,2}" + macSeparator + "){1,5})[0-9,A-F,a-f]{1,2})";
        Pattern pattern = Pattern.compile(regExp);
        Matcher matcher = pattern.matcher(sourceString);
        while (matcher.find()) {
            result = matcher.group(1);
            if (sourceString.indexOf(ip) <= sourceString.lastIndexOf(matcher.group(1))) {
                break; // 如果有多个IP,只匹配本IP对应的Mac.
            }
        }
        return result;
    }

    /**
     * 获取访问地址的IP
     */
    public static String getHostAddress(HttpServletRequest request) {
        String ipAddr = "";
        InetAddress address = null;
        try {
            String serverName = request.getServerName();
            if (isboolIp(serverName)) {
                ipAddr = serverName;
            } else {
                address = InetAddress.getByName(serverName);
                ipAddr = address.getHostAddress();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ipAddr;
    }

    /**
     * 获取来访者的主机名称
     *
     * @param ip
     * @return
     */
    public static String getHostName(String ip) {
        InetAddress inet;
        try {
            inet = InetAddress.getByName(ip);
            return inet.getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取访问者IP 在一般情况下使用Request.getRemoteAddr()即可,但是经过nginx等反向代理软件后,这个方法会失效。
     * 

* 本方法先从Header中获取X-Real-IP,如果不存在再从X-Forwarded-For获得第一个IP(用,分割), 如果还不存在则调用Request * .getRemoteAddr()。 * * @param request * @return */ public static String getIpAddr(HttpServletRequest request) { String ip = request.getHeader("X-Real-IP"); if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) { return ip; } ip = request.getHeader("X-Forwarded-For"); if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) { // 多次反向代理后会有多个IP值,第一个为真实IP。 int index = ip.indexOf(','); if (index != -1) { return ip.substring(0, index); } else { return ip; } } else { return request.getRemoteAddr(); } } /** * 获取MAC地址 * * @return 返回MAC地址 */ public static String getMacAddress(String ip) { String macAddress = ""; macAddress = getMacInWindows(ip).trim(); if (macAddress == null || "".equals(macAddress)) { macAddress = getMacInLinux(ip).trim(); } return macAddress; } /** * @param ip 目标ip * @return Mac Address */ private static String getMacInLinux(final String ip) { String result = ""; String[] cmd = {"/bin/sh", "-c", "ping " + ip + " -c 2 && arp -a"}; String cmdResult = callCmd(cmd); result = filterMacAddress(ip, cmdResult, ":"); return result; } /** * @param ip 目标ip * @return Mac Address */ private static String getMacInWindows(final String ip) { String result = ""; String[] cmd = {"cmd", "/c", "ping " + ip}; String[] another = {"cmd", "/c", "arp -a"}; String cmdResult = callCmd(cmd, another); result = filterMacAddress(ip, cmdResult, "-"); return result; } /** * 获取来访者的浏览器版本 * * @param request * @return */ public static String getRequestBrowserInfo(HttpServletRequest request) { String browserVersion = null; String header = request.getHeader("user-agent"); if (header == null || header.equals("")) { return ""; } if (header.indexOf("MSIE 6.0") > 0) { browserVersion = "IE 6"; } else if (header.indexOf("MSIE 7.0") > 0) { browserVersion = "IE 7"; } else if (header.indexOf("MSIE 8.0") > 0) { browserVersion = "IE 8"; } else if (header.indexOf("MSIE 9.0") > 0) { browserVersion = "IE 9"; } else if (header.indexOf("MSIE 10.0") > 0) { browserVersion = "IE 10"; } else if (header.indexOf("rv:11.0") > 0) { browserVersion = "IE 11"; } else if (header.indexOf("Firefox") > 0) { browserVersion = "Firefox"; } else if (header.indexOf("Chrome") > 0) { browserVersion = "Chrome"; } else if (header.indexOf("Safari") > 0) { browserVersion = "Safari"; } else if (header.indexOf("Camino") > 0) { browserVersion = "Camino"; } else if (header.indexOf("Konqueror") > 0) { browserVersion = "Konqueror"; } return browserVersion; } /** * 获取系统版本信息 * * @param request * @return */ public static String getRequestSystemInfo(HttpServletRequest request) { String systenInfo = null; String header = request.getHeader("user-agent"); if (header == null || header.equals("")) { return ""; } // 得到用户的操作系统 if (header.indexOf("NT 6.0") > 0) { systenInfo = "Windows Vista/Server 2008"; } else if (header.indexOf("NT 5.2") > 0) { systenInfo = "Windows Server 2003"; } else if (header.indexOf("NT 5.1") > 0) { systenInfo = "Windows XP"; } else if (header.indexOf("NT 6.0") > 0) { systenInfo = "Windows Vista"; } else if (header.indexOf("NT 6.1") > 0) { systenInfo = "Windows 7"; } else if (header.indexOf("NT 6.2") > 0) { systenInfo = "Windows Slate"; } else if (header.indexOf("NT 6.3") > 0) { systenInfo = "Windows 9"; } else if (header.indexOf("NT 10.0") > 0) { systenInfo = "Windows 10"; } else if (header.indexOf("NT 5") > 0) { systenInfo = "Windows 2000"; } else if (header.indexOf("NT 4") > 0) { systenInfo = "Windows NT4"; } else if (header.indexOf("Me") > 0) { systenInfo = "Windows Me"; } else if (header.indexOf("98") > 0) { systenInfo = "Windows 98"; } else if (header.indexOf("95") > 0) { systenInfo = "Windows 95"; } else if (header.indexOf("Mac") > 0) { systenInfo = "Mac"; } else if (header.indexOf("Unix") > 0) { systenInfo = "UNIX"; } else if (header.indexOf("Linux") > 0) { systenInfo = "Linux"; } else if (header.indexOf("SunOS") > 0) { systenInfo = "SunOS"; } return systenInfo; } /** * 判断是否为合法IP * @return the ip */ public static boolean isboolIp(String ipAddress) { String ip = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}"; Pattern pattern = Pattern.compile(ip); Matcher matcher = pattern.matcher(ipAddress); return matcher.matches(); } public static void main(String[] args) { InetAddress address = null; try { address = InetAddress.getByName("f0rb.iteye.com"); } catch (UnknownHostException e) { e.printStackTrace(); System.out.println("获取失败"); } System.out.println(address.getHostAddress()); } }

接口方法上使用

 

你可能感兴趣的:(后台,java,java)