Nacos Spring Boot开启权限校验下的使用历程

3 篇文章 0 订阅
本文档介绍了在Nacos Spring Boot应用中遇到的权限认证403错误,问题源于排除fastjson后的序列化异常。解决方法包括升级nacos-spring-context和nacos-client到特定版本,以解决反序列化问题并确保权限验证正常。
摘要由CSDN通过智能技术生成

在此我们假设你已经成功启动了nacos(无论是standalone亦或是cluster模式),并且开启了权限校验 nacos.core.auth.enabled=true,如果您对权限校验还不明了,可以翻看 从nacos的权限控制看nacos的配置热加载
这里我们开始使用客户端来开启nacos使用之旅。好吧,先提前告知,一切并不顺利,因为我们盲目的去除了客户端依赖的fastjson…

基于nacos-1.3.3

Nacos SpringBoot Demo

先上代码,再逐步解析吧
假设我们的配置是这样的:
在这里插入图片描述

  • appliaction.yml
spring:
  application:
    name: helloNacos
server:
  port: 9001

nacos:
  config:
    # nacos服务器的地址及端口
    server-addr: xxxx:xx
    # 配置的命名空间
    namespace: xxxxxx
	# 因为服务端开启了权限认证了,所以需要提供用户名和密码
    username: nacos
    password: nacos
    # 分组
    group: staging
    data-id: helloNacos.properties
    # 配置的类型(ConfigType: properties, xml, json, text, html, yaml)
    type: properties
    auto-refresh: true
  • HelloController.java
import com.alibaba.nacos.api.config.annotation.NacosValue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
public class HelloController {

    @NacosValue(value = "${name:xxx}", autoRefreshed = true)
    private String name;

    @RequestMapping(path = "/test", method = RequestMethod.GET)
    @ResponseBody
    public String hello(@RequestParam(value = "greets") String greets) {

        return "hello, " + name;
    }
}
  • HelloNacosApplication.java
import com.alibaba.nacos.spring.context.annotation.config.NacosPropertySource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
// 这个注解即为开启nacos的钥匙
@NacosPropertySource(dataId = "helloNacos.properties", groupId = "staging", autoRefreshed = true)
public class HelloNacosApplication {
    public static void main(String[] args) {
        SpringApplication.run(HellomiceApplication.class, args);
    }
}
  • pom.xml
    <properties>
        <spring-cloud.version>Greenwich.SR6</spring-cloud.version>
        <nacos-config-spring-boot.version>0.2.7</nacos-config-spring-boot.version>
    </properties>

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

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
       
        <dependency>
            <groupId>com.alibaba.boot</groupId>
            <artifactId>nacos-config-spring-boot-starter</artifactId>
            <version>${nacos-config-spring-boot.version}</version>
            <exclusions>
                <!-- 注意这里排除了fastjson,因为由于安全漏洞我们已经不再支持fastjson -->
                <exclusion>
                    <artifactId>fastjson</artifactId>
                    <groupId>com.alibaba</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>nacos-spring-context</artifactId>
                    <groupId>com.alibaba.nacos</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 先别惊讶这里的多此一举操作 -->
        <!-- 排除nacos-spring-context,并重新引入新包 -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-spring-context</artifactId>
            <version>1.0.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>nacos-client</artifactId>
                    <groupId>com.alibaba.nacos</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 排除nacos-client,并重新引入新包 -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>1.3.3</version>
        </dependency>
    </dependencies>

ps:
如果你不介意使用fastjson,那么参考官方示例,只需依赖合适版本的 nacos-config-spring-boot-starter ,当然也就不会出现下文所说的哪些问题了…

nacos客户端鉴权及getConfig流程

nacos通过NacosConfigService类来对配置进行查询修改等,nacos client获取配置是通过Http向server端发送请求,于是NacosConfigService将这个工作交给了ClientWorker, ClientWorker又将这份工作委托给了HttpAgent

private HttpAgent agent;

public NacosConfigService(Properties properties) throws NacosException {
        ...
        agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
        agent.start();
        worker = new ClientWorker(agent, configFilterChainManager, properties);
    }

ServerHttpAgent在创建时,就会调用登录接口,登录成功后会更新SecurityProxy#accessToken,同时还会有个定时任务调用login方法来更新accessToken;
这个accessToken将包含用户的登录信息,用来在服务端进行权限验证;

public ServerHttpAgent(Properties properties) throws NacosException {
        ...
        // 先登录
        securityProxy.login(serverListMgr.getServerUrls());

        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(....);
		// 定时更新token
        executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                securityProxy.login(serverListMgr.getServerUrls());
            }
        }, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
    }

当查询当前namespace及group的配置时,会通过http调用"/nacos/v1/cs/configs"接口,从服务端的代码可知这个接口需要进行权限校验;

/nacos/v1/cs/configs接口的实现, 需要有用户具有READ权限
@GetMapping
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
 public void getConfig(HttpServletRequest request, HttpServletResponse ...)

那么校验就需要accessToken,这个token就需要在客户端调用时注入到header中,具体注入的地方是在:

ServerHttpAgent#injectSecurityInfo
private void injectSecurityInfo(List<String> params) {
       if (StringUtils.isNotBlank(securityProxy.getAccessToken())) {
           params.add(Constants.ACCESS_TOKEN);
           params.add(securityProxy.getAccessToken());
       }
       ...
   }

那么在原生的0.2.7版本的 nacos-config-spring-boot-starter中登录的实现是:

SecurityProxy#login(String server) 
   	HttpClient.HttpResult result = HttpClient.request(url, new ArrayList<String>(2),
               params, body, Charsets.UTF_8.name(), HttpMethod.POST);

    if (result.code != HttpURLConnection.HTTP_OK) {
        SECURITY_LOGGER.error("login failed: {}", JSON.toJSONString(result));
        return false;
    }
    // !!! com.alibaba.fastjson.JSONObject;
    JSONObject obj = JSON.parseObject(result.content);
    if (obj.containsKey(Constants.ACCESS_TOKEN)) {
        accessToken = obj.getString(Constants.ACCESS_TOKEN);
        tokenTtl = obj.getIntValue(Constants.TOKEN_TTL);
        tokenRefreshWindow = tokenTtl / 10;
    }

getConfig调用返回403

开启debug模式启动HelloNacosApplication会发现登录,获取accessToken的登录接口调用成功,但是调用 /nacos/v1/cs/configs 接口时一直抛出异常,实际的返回码是403,拒绝访问,在nacos中导致403通常是权限用户没登录导致的。
增加断点发现:在登陆接口中 http的返回结果中 确实是返回了token,但是在 ServerHttpAgent#injectSecurityInfosecurityProxy.getAccessToken() 返回空…

这是由于fastjson反序列化失败导致的,这是由于事先在我们在依赖中排除fastjson(因为我的私有仓库已经找不到fastjson了)

JSONObject obj = JSON.parseObject(result.content); 

而且的调用login方法的地方catch了这个异常,并且没给任何提示。所以我们什么也感觉不到,明明是调用login接口成功了,但是在后续的使用中就是拿不到accessToken

怎样替换fastjson

事实上,fastjson是由nacos-client引入的,而nacos-client是在nacos-spring-context中引入的,那就尝试升级nacos-spring-context吧,最新版本是1.0.0,其中对应的nacos-client版本为1.3.2,启动后会发现这个版本虽然将fastjosn替换为Jackson了,但是返回结果乱码,依然会反序列化失败;于是把nacos-client单独升级到1.3.3这个版本,对请求的编码做了处理,可以正常返回。
ps:为什么会想到升级nacos-client了,怎么就知道新版nacos-client替换了fastjson了?
因为实现尝试过nacos spring cloud,那个版本完全没有问题,而其中的nacos-client 版本恰好是1.3.3,并且使用Jackson反序列化结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值