SpringCloud使用Zuul微服务网关进行JWT权限认证

本文介绍了如何配置和测试Spring Cloud Zuul作为微服务网关,包括如何转发请求头信息以避免信息丢失,以及如何使用Zuul过滤器实现JWT权限验证,确保只有具有管理员权限的用户才能访问特定接口。
摘要由CSDN通过智能技术生成

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; // 放行
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我三胖哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值