乐优商城(三十)——授权中心

目录

一、无状态登录原理

1.1 什么是有状态

1.2 什么是无状态

1.3 如何实现无状态

1.4 JWT

1.4.1 简介

1.4.2 数据格式

1.4.3 JWT交互流程

1.4.4 非对称加密

1.5 结合Zuul的鉴权流程

1.5.1 没有RSA加密时

1.5.2 结合RSA的鉴权

二、授权中心

2.1 创建授权中心

2.1.1 创建父module

2.1.2 通用module

2.1.3 授权服务

2.2 JWT工具类

2.3 测试工具类

2.4 编写登录授权接口

2.4.1 生成公钥和私钥

2.4.2 Controller

2.4.3 CookieUtils

2.4.4 UserClient

2.4.5 Service

2.4.6 项目结构

2.4.7 测试

2.5 登录页面

2.6 解决cookie写入问题

2.6.1 问题分析

2.6.2 跟踪CookieUtils

2.6.3 解决host地址的变化

2.6.4 再次测试

2.6.5 Zuul的敏感头过滤

2.6.6 最后的测试


一、无状态登录原理

1.1 什么是有状态

有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。

例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,就能识别到对应session,从而找到用户的信息。

缺点是什么?

  • 服务端保存大量数据,增加服务端压力

  • 服务端保存用户状态,无法进行水平扩展

  • 客户端请求依赖服务端,多次请求必须访问同一台服务器

1.2 什么是无状态

微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:

  • 服务端不保存任何客户端请求者信息

  • 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份

带来的好处是什么呢?

  • 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务

  • 服务端的集群和状态对客户端透明

  • 服务端可以任意的迁移和伸缩

  • 减小服务端存储压力

1.3 如何实现无状态

无状态登录的流程:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)

  • 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证

  • 以后每次请求,客户端都携带认证的token

  • 服务端对token进行解密,判断是否有效。

整个登录过程中,最关键的点是什么?

token的安全性

token是识别客户端身份的唯一标示,如果加密不够严密,被人伪造那就完蛋了。

采用何种方式加密才是安全可靠的呢?

采用JWT + RSA非对称加密

1.4 JWT

1.4.1 简介

JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io

GitHub上jwt的java客户端:https://github.com/jwtk/jjwt

1.4.2 数据格式

JWT包含三部分数据:

  • Header:头部,通常头部有两部分信息:

    对头部进行base64加密(可解密),得到第一部分数据

    • 声明类型,这里是JWT

    • 加密算法,自定义

  • Payload:载荷,就是有效数据,一般包含下面信息:

    这部分也会采用base64加密,得到第二部分数据

    • 用户身份信息(注意,这里因为采用base64加密,可解密,因此不要存放敏感信息)

    • 注册声明:如token的签发时间,过期时间,签发人等

  • Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性

生成的数据格式:

1.4.3 JWT交互流程

流程图:

步骤翻译:

  • 1、用户登录

  • 2、服务的认证,通过后根据secret生成token

  • 3、将生成的token返回给浏览器

  • 4、用户每次请求携带token

  • 5、服务端利用公钥解读jwt签名,判断签名有效后,从Payload中获取用户信息

  • 6、处理请求,返回响应结果

因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,完全符合了Rest的无状态规范。

1.4.4 非对称加密

加密技术是对信息进行编码和解码的技术,编码是把原来可读信息(又称明文)译成代码形式(又称密文),其逆过程就是解码(解密),加密技术的要点是加密算法,加密算法可以分为三类:

  • 对称加密,如AES

    • 基本原理:将明文分成N个组,然后使用密钥对各个组进行加密,形成各自的密文,最后把所有的分组密文进行合并,形成最终的密文。

    • 优势:算法公开、计算量小、加密速度快、加密效率高

    • 缺陷:双方都使用同样密钥,安全性得不到保证

  • 非对称加密,如RSA

    • 基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端

      • 私钥加密,持有私钥或公钥才可以解密

      • 公钥加密,持有私钥才可解密

    • 优点:安全,难以破解

    • 缺点:算法比较耗时

  • 不可逆加密,如MD5,SHA

    • 基本原理:加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。

1.5 结合Zuul的鉴权流程

需要注意的是:secret是签名的关键,因此一定要保密,所以放到鉴权中心保存,其它任何服务中都不能获取secret。

1.5.1 没有RSA加密时

在微服务架构中,可以把服务的鉴权操作放到网关中,将未通过鉴权的请求直接拦截,如图:

  • 1、用户请求登录

  • 2、Zuul将请求转发到授权中心,请求授权

  • 3、授权中心校验完成,颁发JWT凭证

  • 4、客户端请求其它功能,携带JWT

  • 5、Zuul将jwt交给授权中心校验,通过后放行

  • 6、用户请求到达微服务

  • 7、微服务将jwt交给鉴权中心,鉴权同时解析用户信息

  • 8、鉴权中心返回用户数据给微服务

  • 9、微服务处理请求,返回响应

缺点:

每次鉴权都需要访问鉴权中心,系统间的网络请求频率过高,效率略差,鉴权中心的压力较大。

1.5.2 结合RSA的鉴权

  • 首先利用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在Zuul和各个微服务

  • 用户请求登录

  • 授权中心校验,通过后用私钥对JWT进行签名加密

  • 返回jwt给用户

  • 用户携带JWT访问

  • Zuul直接通过公钥解密JWT,进行验证,验证通过则放行

  • 请求到达微服务,微服务直接用公钥解析JWT,获取用户信息,无需访问授权中心

二、授权中心

授权中心的主要职责:

  • 用户鉴权:

    • 接收用户的登录请求,通过用户中心的接口进行校验,通过后生成JWT

    • 使用私钥生成JWT并返回

  • 服务鉴权:微服务间的调用不经过Zuul,会有风险,需要鉴权中心进行认证

    • 原理与用户鉴权类似,但逻辑稍微复杂一些(此处不做实现)

因为生成jwt,解析jwt这样的行为以后在其它微服务中也会用到,因此抽取成工具。把鉴权中心进行聚合,一个工具module,一个提供服务的module。

2.1 创建授权中心

2.1.1 创建父module

将打包方式改为pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou</artifactId>
        <groupId>com.leyou.parent</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou.authentication</groupId>
    <artifactId>leyou-authentication</artifactId>
    <packaging>pom</packaging>


</project>

2.1.2 通用module

2.1.3 授权服务

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou-authentication</artifactId>
        <groupId>com.leyou.authentication</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou.authentication</groupId>
    <artifactId>leyou-authentication-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.leyou.authentication</groupId>
            <artifactId>leyou-authentication-common</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>

启动器

package com.leyou.auth;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @Author: 98050
 * @Time: 2018-10-23 20:11
 * @Feature: 授权服务启动器
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LyAuthApplication {
    public static void main(String[] args) {
        SpringApplication.run(LyAuthApplication.class,args);
    }
}

application.yml

server:
  port: 8087
spring:
  application:
    name: auth-service
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
    registry-fetch-interval-seconds: 10
  instance:
    instance-id: ${spring.application.name}:${server.port}
    prefer-ip-address: true  #当你获取host时,返回的不是主机名,而是ip
    ip-address: 127.0.0.1
    lease-expiration-duration-in-seconds: 10 #10秒不发送九过期
    lease-renewal-interval-in-seconds: 5 #每隔5秒发一次心跳

结构:

在leyou-gateway工程的application.yml中,修改路由:

2.2 JWT工具类

在leyou-authentication-common中导入工具类:

需要在leyou-auth-common中引入JWT依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>leyou-authentication</artifactId>
        <groupId>com.leyou.authentication</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.leyou.authentication</groupId>
    <artifactId>leyou-authentication-common</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
        </dependency>

        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>

2.3 测试工具类

在leyou-authentication-common中编写测试类:

public class JwtTest {

    private static final String pubKeyPath = "C:\\tmp\\rsa\\rsa.pub";

    private static final String priKeyPath = "C:\\tmp\\rsa\\rsa.pri";

    private PublicKey publicKey;

    private PrivateKey privateKey;

    @Test
    public void testRsa() throws Exception {
        RsaUtils.generateKey(pubKeyPath, priKeyPath, "234");
    }

    @Before
    public void testGetRsa() throws Exception {
        this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
        this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
    }

    @Test
    public void testGenerateToken() throws Exception {
        // 生成token
        String token = JwtUtils.generateToken(new UserInfo(20L, "jack"), privateKey, 5);
        System.out.println("token = " + token);
    }

    @Test
    public void testParseToken() throws Exception {
        String token = "eyJhbGciOiJSUzI1NiJ9.eyJpZCI6MjAsInVzZXJuYW1lIjoiamFjayIsImV4cCI6MTUzMzI4MjQ3N30.EPo35Vyg1IwZAtXvAx2TCWuOPnRwPclRNAM4ody5CHk8RF55wdfKKJxjeGh4H3zgruRed9mEOQzWy79iF1nGAnvbkraGlD6iM-9zDW8M1G9if4MX579Mv1x57lFewzEo-zKnPdFJgGlAPtNWDPv4iKvbKOk1-U7NUtRmMsF1Wcg";

        // 解析token
        UserInfo user = JwtUtils.getInfoFromToken(token, publicKey);
        System.out.println("id: " + user.getId());
        System.out.println("userName: " + user.getUsername());
    }
}

测试生成公钥和私钥,运行代码(注意将testGetRsa方法注释掉):

查看目标目录:

公钥和私钥已经生成了!

测试生成token,把@Before的注释去掉:

测试解析token:

正常解析:

JWT工具类

2.4 编写登录授权接口

接下来,需要在leyou-auth-servcice编写一个接口,对外提供登录授权服务。基本流程如下:

  • 客户端携带用户名和密码请求登录

  • 授权中心调用客户中心接口,根据用户名和密码查询用户信息

  • 如果用户名密码正确,能获取用户,否则为空,则登录失败

  • 如果校验成功,则生成JWT并返回

2.4.1 生成公钥和私钥

在授权中心生成真正的公钥和私钥。所以必须有一个生成公钥和私钥的secret,这个可以配置到application.yml中:

然后编写属性类,加载这些数据:

package com.leyou.auth.properties;

import com.leyou.auth.utils.RsaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;

import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * @Author: 98050
 * @Time: 2018-10-23 22:20
 * @Feature: jwt配置参数
 */
@ConfigurationProperties(prefix = "leyou.jwt")
public class JwtProperties {

    /**
     * 密钥
     */
    private String secret;

    /**
     * 公钥地址
     */
    private String pubKeyPath;

    /**
     * 私钥地址
     */
    private String priKeyPath;

    /**
     * token过期时间
     */
    private int expire;

    /**
     * 公钥
     */
    private PublicKey publicKey;

    /**
     * 私钥
     */
    private PrivateKey privateKey;

    private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);

    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public String getPubKeyPath() {
        return pubKeyPath;
    }

    public void setPubKeyPath(String pubKeyPath) {
        this.pubKeyPath = pubKeyPath;
    }

    public String getPriKeyPath() {
        return priKeyPath;
    }

    public void setPriKeyPath(String priKeyPath) {
        this.priKeyPath = priKeyPath;
    }

    public int getExpire() {
        return expire;
    }

    public void setExpire(int expire) {
        this.expire = expire;
    }

    public PublicKey getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(PublicKey publicKey) {
        this.publicKey = publicKey;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }

    /**
     * @PostConstruct :在构造方法执行之后执行该方法
     */
    @PostConstruct
    public void init(){
        try {
            File pubKey = new File(pubKeyPath);
            File priKey = new File(priKeyPath);
            if (!pubKey.exists() || !priKey.exists()) {
                // 生成公钥和私钥
                RsaUtils.generateKey(pubKeyPath, priKeyPath, secret);
            }
            // 获取公钥和私钥
            this.publicKey = RsaUtils.getPublicKey(pubKeyPath);
            this.privateKey = RsaUtils.getPrivateKey(priKeyPath);
        } catch (Exception e) {
            logger.error("初始化公钥和私钥失败!", e);
            throw new RuntimeException();
        }
    }
}

2.4.2 Controller

编写授权接口,接收用户名和密码,校验成功后,写入cookie中。

  • 请求方式:post

  • 请求路径:/accredit

  • 请求参数:username和password

  • 返回结果:无

代码:

注意配置属性类:@EnableConfigurationProperties(JwtProperties.class)

package com.leyou.auth.controller;

import com.leyou.auth.properties.JwtProperties;
import com.leyou.auth.service.AuthService;
import com.leyou.utils.CookieUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Author: 98050
 * @Time: 2018-10-23 22:43
 * @Feature: 登录授权
 */
@Controller
@EnableConfigurationProperties(JwtProperties.class)
public class AuthController {

    @Autowired
    private AuthService authService;

    @Autowired
    private JwtProperties properties;

    @PostMapping("accredit")
    public ResponseEntity<Void> authentication(
            @RequestParam("username") String username,
            @RequestParam("password") String password,
            HttpServletRequest request,
            HttpServletResponse response
    ){
        //1.登录校验
        String token = this.authService.authentication(username,password);
        if (StringUtils.isBlank(token)){
            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
        }
        //2.将token写入cookie,并指定httpOnly为true,防止通过js获取和修改
        CookieUtils.setCookie(request,response,properties.getCookieName(),token,properties.getCookieMaxAge(),true);

        return ResponseEntity.ok().build();
    }
}

2.4.3 CookieUtils

作用,将生成好的token放入到cookie中,返回到客户端。

代码:略

2.4.4 UserClient

对用户密码进行校验,所以需要通过FeignClient去访问 user-service微服务:

引入user-service依赖:

<dependency>
    <groupId>com.leyou.user</groupId>
    <artifactId>leyou-user-interface</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

编写FeignClient:

package com.leyou.auth.client;

import com.leyou.user.api.UserApi;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @Author: 98050
 * @Time: 2018-10-23 23:48
 * @Feature: 用户feignclient
 */
@FeignClient(value = "user-service")
public interface UserClient extends UserApi {
}

在leyou-user-interface中添加api接口:

package com.leyou.user.api;

import com.leyou.user.pojo.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @Author: 98050
 * @Time: 2018-10-23 23:50
 * @Feature: 用户服务接口
 */
public interface UserApi {
    /**
     * 用户验证
     * @param username
     * @param password
     * @return
     */
    @GetMapping("query")
    User queryUser(@RequestParam("username")String username, @RequestParam("password")String password);
}

2.4.5 Service

接口

package com.leyou.auth.service;

/**
 * @Author: 98050
 * @Time: 2018-10-23 22:46
 * @Feature:
 */
public interface AuthService {
    /**
     * 用户授权
     * @param username
     * @param password
     * @return
     */
    String authentication(String username, String password);
}

实现

package com.leyou.auth.service.impl;

import com.leyou.auth.client.UserClient;
import com.leyou.auth.entity.UserInfo;
import com.leyou.auth.properties.JwtProperties;
import com.leyou.auth.service.AuthService;
import com.leyou.auth.utils.JwtUtils;
import com.leyou.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @Author: 98050
 * @Time: 2018-10-23 22:47
 * @Feature:
 */
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private UserClient userClient;

    @Autowired
    private JwtProperties properties;

    /**
     * 用户授权
     * @param username
     * @param password
     * @return
     */
    @Override
    public String authentication(String username, String password) {

        try{
            //1.调用微服务查询用户信息
            User user = this.userClient.queryUser(username,password);
            System.out.println(user);
            //2.查询结果为空,则直接返回null
            if (user == null){
                return null;
            }
            //3.查询结果不为空,则生成token
            String token = JwtUtils.generateToken(new UserInfo(user.getId(), user.getUsername()),
                    properties.getPrivateKey(), properties.getExpire());
            System.out.println(token);
            return token;

        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}

2.4.6 项目结构

2.4.7 测试

token已经生成,返回到客户端。

2.5 登录页面

请求路径不对,因为认证接口是:

/api/auth/accredit

修改login.html:

然后登录,可以跳转到首页。

2.6 解决cookie写入问题

查看首页cookie:

2.6.1 问题分析

之前测试时,看到了响应头中,有Set-Cookie属性,为什么在这里却什么都没有?

因为涉及到跨域问题。跨域请求cookie生效的条件:

  • 服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。

  • 响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名

  • 浏览器发起ajax需要指定withCredentials 为true

服务端cors配置:

没有问题。

客户端ajax配置:common.js中

也没有问题。

那说明,问题一定出在响应的set-cookie头中。仔细查看刚才的响应头:

cookie的 domain属性似乎不太对

cookie也是有 的限制,一个网页,只能操作当前域名下的cookie,但是现在看到的地址是0.0.1(为什么是0.0.1,因为前端的地址是www.leyou.com,通过nginx反向代理到127.0.0.1,通过计算得到domain为0.0.1),而页面是www.leyou.com,域名不匹配,cookie设置肯定失败了!

2.6.2 跟踪CookieUtils

再次登录,然后进行Debug

CookieUtils内部有一个获取domain的方法:

它获取domain是通过服务器的host来计算的,然而我们的地址竟然是:127.0.0.1:8087,因此后续的运算,最终得到的domain就变成了:

问题找到了:

请求时的serverName是:api.leyou.com,现在却被变成了127.0.0.1:,因此计算domain是错误的,从而导致cookie设置失败!

2.6.3 解决host地址的变化

那么问题来了:为什么这里的请求serverName变成了:127.0.0.1:8087呢?

这里的server name其实就是请求时的主机名:Host,之所以改变,有两个原因:

  • 使用了nginx反向代理,当监听到api.leyou.com的时候,会自动将请求转发至127.0.0.1:10010,即Zuul。

  • 而后请求到达我们的网关Zuul,Zuul就会根据路径匹配,我们的请求是/api/auth,根据规则被转发到了 127.0.0.1:8087 ,即授权中心。

所以,先更改nginx配置,让它不要修改host:proxy_set_header Host $host;

nginx重启。

然后再去解决Zuul的问题,因为Zuul还会有一次转发,所以要去修改网关的配置(leyou-gateway):

重启项目,再次测试:

最后计算得到domain:

2.6.4 再次测试

还是没有cookie!

2.6.5 Zuul的敏感头过滤

Zuul内部有默认的过滤器,会对请求和响应头信息进行重组,过滤掉敏感的头信息:

会发现,这里会通过一个属性为SensitiveHeaders的属性,来获取敏感头列表,然后添加到IgnoredHeaders中,这些头信息就会被忽略。

而这个SensitiveHeaders的默认值就包含了set-cookie

解决方案有两种:

全局设置:

  • zuul.sensitive-headers=

指定路由设置:

  • zuul.routes.<routeName>.sensitive-headers=

  • zuul.routes.<routeName>.custom-sensitive-headers=true

思路都是把敏感头设置为null

2.6.6 最后的测试

再次重启,登录:

猜你喜欢

转载自blog.csdn.net/lyj2018gyq/article/details/83311809
今日推荐