拦截器
该拦截器实现HandlerInterceptor接口,可根据业务重写方法实现请求拦截,这里我的代码采用的是对一个自定义
请求头拦截
preHandle:在服务层处理之前被调用,一般用户鉴权,token验证,uri或者ip拦截
postHandle:服务层调用之后,返回视图之前被调用
afterCompletion:在DispatcherServlet完全处理完请求后被调用
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return request.getHeader("reqid") != null && request.getHeader("reqid").equals("dkh");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
需要将拦截器注册到容器中,并设置跨域
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.addExposedHeader("*");
UrlBasedCorsConfigurationSource corsConfigurationSource =
new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
return new CorsFilter(corsConfigurationSource);
}
@Bean
public AuthInterceptor initAuthInterceptor(){
return new AuthInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(initAuthInterceptor()).addPathPatterns("/**");
}
}
数据加密
在进行接口数据之前,前后端应进行商讨采用何种加密方式、加密密钥、解密规则
这里博主采用AES/ECB/PKCS5Padding加密规则
private static final String KEY = "xxxxxxxxxxxxxxx";
private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";
public static String base64Encode(byte[] bytes){
return Base64.encodeBase64String(bytes);
}
public static String aesEncrypt(String content) {
try {
return aesEncrypt(content, KEY);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public static byte[] aesEncryptToBytes(String content, String encryptKey)throws Exception
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
Cipher cipher = Cipher.getInstance(ALGORITHMSTR);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));
return cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
}
public static String aesEncrypt(String content, String encryptKey) throws Exception {
return base64Encode(aesEncryptToBytes(content, encryptKey));
}
前端解密
export function decrypt(word){
const key = CryptoJS.enc.Utf8.parse("xxxxxxxxxxxxxxx");
const decrypt = CryptoJS.AES.decrypt(word, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
}
请求限制
针对恶意请求的ip进行封禁,这里使用的是自定义注解并结合AOP,将用户的请求ip和uri存入内存,并设置过期时间
也可以使用redis进行缓存处理
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestrictionRequest {
long time() default 3000;
int count() default 3;
}
AOP
import com.dkh.common.annotation.RestrictionRequest;
import com.dkh.entity.AjaxResult;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class RestrictionRequestAspect {
private static ConcurrentHashMap<String, ExpiringMap<String, Integer>> book = new ConcurrentHashMap<>();
@Pointcut("@annotation(RestrictionRequest)")
public void excudeService(RestrictionRequest limitRequest) {
}
@Around("excudeService(RestrictionRequest)")
public Object doAround(ProceedingJoinPoint pjp, RestrictionRequest limitRequest) throws Throwable {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
System.out.println("url="+request.getRequestURL()+";User-Agent="+request.getHeader("User-Agent"));
ExpiringMap<String, Integer> uc = book.getOrDefault(request.getRequestURI(), ExpiringMap.builder().variableExpiration().build());
Integer uCount = uc.getOrDefault(request.getHeader("User-Agent"), 0);
if (uCount >= limitRequest.count()) {
return AjaxResult.success("接口访问过多!");
} else if (uCount == 0){
uc.put(request.getRemoteAddr(), uCount + 1, ExpirationPolicy.CREATED, limitRequest.time(), TimeUnit.MILLISECONDS);
} else {
uc.put(request.getRemoteAddr(), uCount + 1);
}
book.put(request.getRequestURI(), uc);
return pjp.proceed();
}
}