假如我们现在有一个需求,权限的校验,要在所有的请求多一个tonken参数,
如果按着原来的做法是不是得每一个接口都要先校验一遍呢
http://192.168.66.63:30600/postman/eurekaApi/test?token=123
下面我用Zuul来实现校验所有请求必须加入一个token,并且值不为空,开始搞事情
首先在Zuul服务上建立一个类并继承ZuulFilter
这得实现四个方法,这时候我们需要用到一个常量类
Eclipse查询类的快捷是C+S+H
这个类貌似是专门提供校验规则的,来写写
package com.sola.filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class TokenFilter extends ZuulFilter{
@Override
public Object run() throws ZuulException {
// 这里就是我们要处理的逻辑代码编写处了
//这个是Zuul自己封装的 看清导入的包
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//再从参数里去拿token,也可以从Cookie和header里获取
String token = request.getParameter("token");
//判断token是否为空
if(StringUtils.isEmpty(token)){
//往Zuul设置没有权限
currentContext.setSendZuulResponse(false);
//再设置一个状态码,权限不足一般是401
currentContext.setResponseStatusCode(401);
}
return true;
}
@Override
public boolean shouldFilter() {
// 个人理解这个应该是设置过滤器是否执行。。
// 默认false 改成true
return true;
}
@Override
public int filterOrder() {
//这个呢就是顺序,一种过滤器(前置)会都很多道过滤器
//这个就是指定道放在哪个位置执行,数值越小的越靠前
//暂时可以放到这个之前 那么我们可以 -1来实现
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public String filterType() {
//下面实现参数校验,所以用这个常量
//这人理解这个应该是指定过滤器的类型(之前讲过的前置,后置)
//这里指的前置
return FilterConstants.PRE_TYPE;
}
}
之后我们去源地址访问一下,不加Token
加Token
————————————————————————————————————————————————————————
下面来写一个后置过滤器,再处理完的结果进行加工
package com.sola.filter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
@Component
public class BeforFilter extends ZuulFilter{
@Override
public Object run() throws ZuulException {
// 搞事情
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletResponse response = currentContext.getResponse();
response.setHeader("solaHeader", "213213213");
return null;
}
@Override
public boolean shouldFilter() {
// 个人理解这个应该是设置过滤器是否执行。。
// 默认false 改成true
return true;
}
@Override
public int filterOrder() {
// 这个需要放在他之前,个人感觉不放他之前就发给前端了= =
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
public String filterType() {
//下面实现参数校验,所以用这个常量
//这人理解这个应该是指定过滤器的类型(之前讲过的前置,后置)
//这里指的后置
return FilterConstants.POST_TYPE;
}
}
加入了一个请求头
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Zuul限流
为什么要限流呢,比如说有一个接口是发短信的,这时候就要限制客户访问量,防止造成短信轰炸
Zuul的前置过滤器有限流,又有鉴权
限流应该早于鉴权
下面介绍一种令牌桶限流
令牌桶这个算法 guava(谷歌开源的jar)已经实现了,我们直接拿来用就可以
先来加个依赖
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>26.0-jre</version>
</dependency>
代码
package com.sola.filter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.sola.exception.RateLimitException;
/**
* @author JAVA
* 限流
*/
@Component
public class RateFilter extends ZuulFilter{
//这就是去调用的Guava实现的,参数是每秒放入多少个令牌
private static final RateLimiter ratelimiter = RateLimiter.create(100);
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
//判断是否拿到令牌
if(!ratelimiter.tryAcquire()){
//这里我们可以定义一个异常
throw new RateLimitException();
}
return null;
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
// 限流就要做到优先级最高 比最高还要高 最高是-3
return FilterConstants.SERVLET_DETECTION_FILTER_ORDER - 1;
}
}
package com.sola.exception;
public class RateLimitException extends RuntimeException{
}
理论上完成了
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
鉴权和添加用户服务
代码写一写
package com.sola.filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
/**
* @author JAVA
* 权限拦截
* postman 只能访问postman
* roadwork 只能访问roadwork
* config server 都能访问
*/
@Component
public class AuthFilter extends ZuulFilter{
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//检查地址是否包含postman
if(request.getRequestURI().contains("postman")){
//获取rule值
String parameter = request.getParameter("rule");
//判断是否为空
if(StringUtils.isEmpty(parameter) || parameter == null){
//空就是设置没权限
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
}else{
//判断 全权限是否是postman权限 没有就滚蛋
if(!parameter.equals("postman")){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
}
}
}
if(request.getRequestURI().contains("roadwork")){
//获取rule值
String parameter = request.getParameter("rule");
//判断是否为空
if(StringUtils.isEmpty(parameter) || parameter == null){
//空就是设置没权限
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
}else{
//判断 全权限是否是postman权限 没有就滚蛋
if(!parameter.equals("roadwork")){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
}
}
}
return null;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
}
这样写是好写了,但是相当不好维护,可以稍微改变一下结构,每一个需要的权限单独写一个filter,看起来就好多了
package com.sola.filter;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
/**
* @author JAVA
* 权限拦截
* postman 只能访问postman
*/
@Component
public class AuthFilter extends ZuulFilter{
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//获取rule值
String parameter = request.getParameter("rule");
//判断是否为空
if(StringUtils.isEmpty(parameter) || parameter == null){
//空就是设置没权限
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
}else{
//判断 全权限是否是postman权限 没有就滚蛋
if(!parameter.equals("postman")){
currentContext.setSendZuulResponse(false);
currentContext.setResponseStatusCode(401);
}
}
return null;
}
@Override
public boolean shouldFilter() {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
//检查地址是否包含postman
if(request.getRequestURI().contains("postman")){
//如果包含就去执行run 不包含就跳别的过滤器
return true;
}
return false;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
}
package com.sola.config;
import java.util.ArrayList;
import java.util.Arrays;
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;
/**
* @author JAVA
* 解决跨域
*/
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter(){
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
//配置是否支持cookie跨域
config.setAllowCredentials(true);
//配置原始域,也可以指定www.a.com等等
config.setAllowedOrigins(Arrays.asList("*"));
//设置允许的头
config.setAllowedHeaders(Arrays.asList("*"));
//设置方法(get,post等)
config.setAllowedMethods(Arrays.asList("*"));
//设置缓存时间,同时间内不再检查500秒
config.setMaxAge(500l);
//对哪些域名进行配置,设置为所有
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}