org.springframework.boot
spring-boot-starter-parent
1.3.5.RELEASE
org.springframework.cloud
spring-cloud-dependencies
Brixton.SR3
pom
import
org.springframework.cloud
spring-cloud-starter-zuul
@SpringBootApplication
@EnableZuulProxy
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#在具体的开发dev、测试test、生产prod为后缀的文件中配置个性化的服务端口等信息
spring.profiles.active=dev
##超时设置
zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=10000
#文件上传大小配置
spring.http.multipart.maxFileSize=5Mb
spring.http.multipart.maxRequestSize=10Mb
#####################################
# 为了避免路由表出现在多个配置文件,现在将路由表统一移至这里
#####################################
#定义各个服务的路由名称 比如对于请求client系统url会用${server.client}标识
zuul.routes.client.path=/client-service/**
zuul.routes.client.url=${server.client}/
swagger.butler.resources.client.name=client-api
zuul.routes.uums.path=/uums-service/**
zuul.routes.uums.url=${server.uums}/
swagger.butler.resources.uums.name=uums-api
#####################################
#
# 登录及获取用户基本信息路由表(实际不需要路由)--登录登出等特殊的几个接口处理
#
#####################################
#用户登录 在拦截器中做了相关权限验证,转发
zuul.routes.login.path=/api/login/**
#获取用户基本信息
zuul.routes.getUserInfo.path=/api/secret/getUserInfo/**
#获取用户最新基本信息(用户身份信息变更场景)
zuul.routes.getLatestUserInfo.path=/api/secret/getLatestUserInfo/**
#用户登出
zuul.routes.logout.path=/api/secret/logout/**
#####################################
# 注册服务路由表
#####################################
zuul.routes.regcheckMobile.path=/api/open/reg/checkMobile/**
zuul.routes.regcheckMobile.url=${server.uums}/reg/checkMobile
zuul.routes.registerWithJdxt.path=/api/secret/reg/registerWithJdxt/**
zuul.routes.registerWithJdxt.url=${server.uums}/reg/registerWithJdxt
#也可以给一个系统或者一类应用做统一的匹配 它会将以/api/secret/order/**开始的接口转发到
#${server.order}/**系统中
zuul.routes.order.path=/api/secret/order/**
zuul.routes.order.url=${server.order}/
开发文件配置:
#服务器端口设置
server.port=8666
debug=false
#开启路由自动回调
#router.debug=true
router.debug.force=true
feign.httpclient.enabled=true
#服务器地址统一管理
server.uums=http://172.16.16.68:8088
server.admin=http://172.16.16.68:8086
server.client=http://172.16.16.68:8090
server.aces=http://172.16.16.68:8092
server.admin.lxxd=http://172.16.16.68:8096
server.order=http://172.16.16.45:8800
server.warning=http://172.16.16.45:8900
server.sxt=http://192.168.8.199:8070
##########################################################
#redis 相关设置
##########################################################
#spring-boot-redis 自动配置参数: RedisAutoConfiguration RedisProperties
#注意:当前redis server 没有配置访问密码,因此,启动参数必须设置为非保护模式:
#./redis-server redis.conf --protected-mode no
spring.redis.protected-mode=no
spring.redis.database=0
spring.redis.url=
spring.redis.host=172.16.16.36
spring.redis.password=
spring.redis.port=6379
spring.redis.ssl=false
spring.redis.timeout=2000
spring.redis.pool.maxIdle=8
spring.redis.pool.minIdle=0
spring.redis.pool.maxActive=8
spring.redis.pool.maxWait=-1
关于listOfServers配置负载均衡以及ribbon.eureka等的配置有待学习
配置filter(filter规则后面详细说明)
package ixinnuo.financial.gateway.router;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import ixinnuo.financial.gateway.utils.Util;
/**
*前置拦截器--权限校验(不需权限直接通过)
**/
@Component
public class RouterPreOpen extends ZuulFilter{ //需继承zuul提供的过滤器类
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() { //返回为true才会进入run()方法执行拦截
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
return (boolean)request.getAttribute("shouldFilter");
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("得到的url为:"+request.getRequestURL());
String urlPath = Util.getUrlPath(request.getRequestURL().toString());
if(urlPath.toLowerCase().startsWith("/api/open/")){
logger.info("[进入API网关过滤器][开放接口,不需token,直接前往微服务层]"); //将拦截结果添加到请求中,若不需要走下面的过滤器,可赋值为false,反之为true
request.setAttribute("shouldFilter", false);
return null;
}
return null;
}
}
package ixinnuo.financial.gateway.router;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import ixinnuo.financial.gateway.framework.Code;
import ixinnuo.financial.gateway.framework.RedisDao;
import ixinnuo.financial.gateway.framework.ReturnData;
import ixinnuo.financial.gateway.utils.HttpClientUtil;
import ixinnuo.financial.gateway.utils.Util;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/**
*前置过滤器--需要权限的校验
**/
@Component
public class RouterPreSecret extends ZuulFilter{
private Logger logger = LoggerFactory.getLogger(this.getClass());
private final String TOKEN_PREFIX= "API_GATEWAY_ACCESS_TOKEN:";
private final int ONE_MINUTE= 60;
@Value(value="${server.uums}")
String uums_url;
@Autowired
private RedisDao redisDao; //存储用户信息,用做有效时长内用户权限的检验
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 2;
}
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
return (boolean)request.getAttribute("shouldFilter");
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String urlPath = Util.getUrlPath(request.getRequestURL().toString());
//针对secret需要相关权限验证的请求做相关判断
if(urlPath.toLowerCase().startsWith("/api/secret/")){
Enumeration headerNames = request.getHeaderNames();
String accessToken = "";
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = request.getHeader(key);
logger.debug("[Headers][{}][{}]",key,value);
if("token".equalsIgnoreCase(key)){
accessToken = value;
break;
}
}
if(StringUtils.isBlank(accessToken)) {//说明认证信息不全,不予通过权限
ReturnData retData = new ReturnData(Code.TOKEN_EXPIRE_OR_INVALID);
JSONObject jo = JSONObject.fromObject(retData);
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(200);
ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");
try {
ctx.getResponse().getWriter().write(jo.toString());
}catch (Exception e) {}
logger.info("[没有携带Token,拒绝访问]");
request.setAttribute("shouldFilter", false);
return null;
}
String userInfoString = redisDao.getStringValue(TOKEN_PREFIX+accessToken);
if(StringUtils.isBlank(userInfoString)){
ReturnData retData = new ReturnData(Code.TOKEN_EXPIRE_OR_INVALID);
JSONObject jo = JSONObject.fromObject(retData);
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(200);
ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");
try {
ctx.getResponse().getWriter().write(jo.toString());
}catch (Exception e){}
logger.info("[用Token换取的用户基本信息为空,拒绝访问]");
request.setAttribute("shouldFilter", false);
return null;
}
if(urlPath.toLowerCase().startsWith("/api/secret/getuserinfo")){
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(200);
ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");
try {
ctx.getResponse().getWriter().write(userInfoString);
}catch (Exception e){}
logger.info("[获取用户基本信息成功: {}]",userInfoString);
request.setAttribute("shouldFilter", false);
return null;
}else if(urlPath.toLowerCase().startsWith("/api/secret/logout")){
redisDao.deleteKey(TOKEN_PREFIX+accessToken);
ReturnData retData = new ReturnData(Code.OK);
JSONObject jo = JSONObject.fromObject(retData);
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(200);
ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");
try {
ctx.getResponse().getWriter().write(jo.toString());
}catch (Exception e){}
logger.info("[用户登出成功,Token: {}]",accessToken);
request.setAttribute("shouldFilter", false);
return null;
}
byte[] bytes = null;
try {
bytes = userInfoString.getBytes("utf-8");
}catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
@SuppressWarnings("restriction")
String base64 = new sun.misc.BASE64Encoder().encode(bytes);
base64 = base64.replaceAll("\r|\n", "");
ctx.addZuulRequestHeader("loginInfo", base64);
logger.info("[即将前往微服务层,携带经过base64后的loginInfo: {}]",userInfoString);
request.setAttribute("shouldFilter", false);
return null;
}
request.setAttribute("shouldFilter", true);
return null;
}
}
此外,还需要登录拦截器,匹配到登录的路由,进行登录请求,取得用户信息,并存储在redis中对应的时长,以做后面权限的校验。