1. 环境配置
pom.xml
<!-- 微服务网关 Zuul-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
Application 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy // 开启微服务网关代理
public class ManagerApplication {
public static void main(String[] args) {
SpringApplication.run(ManagerApplication.class);
}
}
application.yml
server:
port: 9011
spring:
application:
name: sunmone-manager
zuul: # 微服务网关配置
routes:
sunmone-qa: # 名字随便起
path: /qa/** # path固定写法 : 配置请求URL的规则,qa微服务以qa为开头
serviceId: sunmone-qa # serviceId固定写法 : 指定Eureka注册中心的服务名称 必须一致
sunmone-base: # 名字随便起
path: /base/** # path固定写法 : 配置请求URL的规则
serviceId: sunmone-base # serviceId固定写法 : 指定Eureka注册中心的服务名称 必须一致
sunmone‐article: #文章
path: /article/** #配置请求URL的请求规则
serviceId: sunmone‐article #指定Eureka注册中心中的服务id
sunmone‐friend: #交友
path: /friend/** #配置请求URL的请求规则
serviceId: sunmone‐friend #指定Eureka注册中心中的服务id
sunmone‐spit: #吐槽
path: /spit/** #配置请求URL的请求规则
serviceId: sunmone‐spit #指定Eureka注册中心中的服务id
sunmone‐user: #用户
path: /user/** #配置请求URL的请求规则
serviceId: sunmone‐user #指定Eureka注册中心中的服务
2. 测试微服务网关
配置好网关微服务后,我们只需要访问9011这一个网关地址,加上配置的 path/路径
就可以不用区分端口号来访问微服务了
访问 9003 的问答微服务,那么我们只需要访问 http://localhost:9011/qa/problem/
就可以了
可以看到通过网关转发请求,访问成功返回
3. 使用Zuul过滤器转发请求头信息
使用Zuul过滤器转发请求会导致请求头中的信息丢失,那么如何保证请求头信息不丢失呢,参考代码
创建过滤器
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class WebFilter extends ZuulFilter { // 继承Zuul过滤器
/**
* pre: 可以在请求被路由之前调用
* route: 在路由请求时候被调用
* post: 在route和error过滤器之后被调用
* error: 处理请求时发生错误时被调用
* @return
*/
@Override
public String filterType() {
return "pre"; // 前置过滤器
}
@Override
public int filterOrder() {
return 0; // 优先级顺序,如果有多个过滤器,数值越小优先级越高
}
@Override
public boolean shouldFilter() {
return true; // 过滤器开关,默认false为关闭,需要改成true才开启
}
/**
* 过滤器的具体执行内容
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
System.out.println("经过了过滤器...");
// 获取zuul的 容器
RequestContext currentContext = RequestContext.getCurrentContext();
// 获取 request
HttpServletRequest request = currentContext.getRequest();
// 获取 request中的头信息 token
String authorization = request.getHeader("Authorization");
// 把头信息放到request中经过过滤器才不会丢失
if (authorization != null){
// 把数据放到Zuul请求头中,在controller层也可以用request获取
currentContext.addZuulRequestHeader("Authorization",authorization);
}
return null; // 放行
}
}
4. 使用Zuul实现jwt权限验证
配置和上面环境配置一样,只需要加上jwt的依赖和配置即可
pom.xml
<!--jwt 依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
application.yml
jwt:
config:
key: sunmone
jwt 工具类
package utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
@ConfigurationProperties("jwt.config") // 在 application.yml 中取 key 和 ttl的值
public class JwtUtil {
private String key ;
private long ttl ;//一个小时
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id
* @param subject
* @return
*/
public String createJWT(String id, String subject, String roles) {
// 获取当前时间毫秒值
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
// 创建 token 令牌
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject) // jwt 指定的用户
.setIssuedAt(now) // jwt 签发时间
.signWith(SignatureAlgorithm.HS256, key).claim("roles", roles); //jwt 头部 和自定义角色权限
// 如果 ttl大于0 设置token令牌过期时间
if (ttl > 0) {
builder.setExpiration( new Date( nowMillis + ttl));
}
// 返回 token 令牌
return builder.compact();
}
/**
* 解析JWT
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr){
// 解析令牌
return Jwts.parser()
.setSigningKey(key) // 令牌的 盐
.parseClaimsJws(jwtStr) // token 值
.getBody();
}
}
4.2 创建Filter过滤器
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import utils.JwtUtil;
import javax.servlet.http.HttpServletRequest;
@Component // 加载容器
public class ManagerFilter extends ZuulFilter {
@Autowired
private JwtUtil jwtUtil;
/**
* pre: 可以在请求被路由之前调用
* route: 在路由请求时候被调用
* post: 在route和error过滤器之后被调用
* error: 处理请求时发生错误时被调用
*
* @return
*/
@Override
public String filterType() {
return "pre"; // 前置过滤器
}
@Override
public int filterOrder() {
return 0; // 优先级顺序,如果有多个过滤器,数值越小优先级越高
}
@Override
public boolean shouldFilter() {
return true; // 过滤器开关,默认false为关闭,需要改成true才开启
}
/**
* 过滤器的具体执行内容
*
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
// 获取zuul的 容器
RequestContext currentContext = RequestContext.getCurrentContext();
// 获取 request
HttpServletRequest request = currentContext.getRequest();
// 放行跨域请求,请求在内部跨域调用会变成 OPTIONS 请求
if (request.getMethod().equals("OPTIONS")) {
return null;
}
// 放行管理员登陆请求
String requestURL = request.getRequestURL().toString();
if (requestURL.contains("/login")) {
return null;
}
/* 判断 Token是否为管理员 */
// 获取 request中的头信息 token
String authorization = request.getHeader("Authorization");
// 如果头信息中的 Authorization 不为null不为空
if (authorization != null && !authorization.equals("")) {
// 如果请求头 Authorization 携带的数据以Bearer开头并且长度大于8
// (前后端约定 请求头 Authorization : Bearer空格+token)为token信息
if (authorization.startsWith("Bearer ") && authorization.length() > 8) {
// 那么把Token取出
String token = authorization.substring(7);
// 使用工具类解析 Token
Claims claims = jwtUtil.parseJWT(token);
// 判断Token是否为管理员权限
if (claims.get("roles").equals("admin")) {
// 把请求头信息重新放到zuul请求中
currentContext.addZuulRequestHeader("Authorization", authorization);
return null;
}
}
}
// 如果认证失败,返回401 权限不足
currentContext.setResponseStatusCode(401);
currentContext.setResponseBody("权限不足");
// 设置返回格式
currentContext.getResponse().setContentType("text/html;charset=UTF-8");
// 拒绝放行
currentContext.setSendZuulResponse(false);
return null; // 放行
}
}