暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

第四篇:Nacos Server 详探

重塑之路 2020-05-02
1269
上文我们在参考源码样例的基础上(传送门),成功启动了自己的Nacos Server,本片文章我们在上一篇的基础上,继续详细步骤,来理解Nacos Server 使用的步骤和每部分的功能,为下一步自己的封装做好准备。
设置maven
我们先做好maven配置,在idea中,File-->Settings

在settings.xml中,我们修改两个地方:
1、设置自己maven仓库位置,默认是C盘
在localRepository节点中修改自己想存放的位置(这里存放的是项目依赖的jar包
<localRepository>D:\Tools\maven_repo</localRepository>

1、修改仓库源
在mirrors节点中添加
     <mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>



创建项目
在idea中 File-->New-->Project

创建完毕,项目结构:

启动NacosApplication.java

"C:\Program Files\Java\jdk1.8.0_111\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:57519,suspend=y,server=n -XX:TieredStopAtLevel=1 -noverify -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -javaagent:C:\Users\back_\AppData\Local\JetBrains\IntelliJIdea2020.1\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_111\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar;D:\ProjectWork\reims\nacos\target\classes;D:\Tools\maven_repo\org\springframework\boot\spring-boot-starter-web\2.2.6.RELEASE\spring-boot-starter-web-2.2.6.RELEASE.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot-starter\2.2.6.RELEASE\spring-boot-starter-2.2.6.RELEASE.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot\2.2.6.RELEASE\spring-boot-2.2.6.RELEASE.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot-autoconfigure\2.2.6.RELEASE\spring-boot-autoconfigure-2.2.6.RELEASE.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot-starter-logging\2.2.6.RELEASE\spring-boot-starter-logging-2.2.6.RELEASE.jar;D:\Tools\maven_repo\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\Tools\maven_repo\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\Tools\maven_repo\org\apache\logging\log4j\log4j-to-slf4j\2.12.1\log4j-to-slf4j-2.12.1.jar;D:\Tools\maven_repo\org\apache\logging\log4j\log4j-api\2.12.1\log4j-api-2.12.1.jar;D:\Tools\maven_repo\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\Tools\maven_repo\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\Tools\maven_repo\org\yaml\snakeyaml\1.25\snakeyaml-1.25.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot-starter-json\2.2.6.RELEASE\spring-boot-starter-json-2.2.6.RELEASE.jar;D:\Tools\maven_repo\com\fasterxml\jackson\core\jackson-databind\2.10.3\jackson-databind-2.10.3.jar;D:\Tools\maven_repo\com\fasterxml\jackson\core\jackson-annotations\2.10.3\jackson-annotations-2.10.3.jar;D:\Tools\maven_repo\com\fasterxml\jackson\core\jackson-core\2.10.3\jackson-core-2.10.3.jar;D:\Tools\maven_repo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.10.3\jackson-datatype-jdk8-2.10.3.jar;D:\Tools\maven_repo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.10.3\jackson-datatype-jsr310-2.10.3.jar;D:\Tools\maven_repo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.10.3\jackson-module-parameter-names-2.10.3.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot-starter-tomcat\2.2.6.RELEASE\spring-boot-starter-tomcat-2.2.6.RELEASE.jar;D:\Tools\maven_repo\org\apache\tomcat\embed\tomcat-embed-core\9.0.33\tomcat-embed-core-9.0.33.jar;D:\Tools\maven_repo\org\apache\tomcat\embed\tomcat-embed-el\9.0.33\tomcat-embed-el-9.0.33.jar;D:\Tools\maven_repo\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.33\tomcat-embed-websocket-9.0.33.jar;D:\Tools\maven_repo\org\springframework\boot\spring-boot-starter-validation\2.2.6.RELEASE\spring-boot-starter-validation-2.2.6.RELEASE.jar;D:\Tools\maven_repo\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;D:\Tools\maven_repo\org\hibernate\validator\hibernate-validator\6.0.18.Final\hibernate-validator-6.0.18.Final.jar;D:\Tools\maven_repo\org\jboss\logging\jboss-logging\3.4.1.Final\jboss-logging-3.4.1.Final.jar;D:\Tools\maven_repo\com\fasterxml\classmate\1.5.1\classmate-1.5.1.jar;D:\Tools\maven_repo\org\springframework\spring-web\5.2.5.RELEASE\spring-web-5.2.5.RELEASE.jar;D:\Tools\maven_repo\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Tools\maven_repo\org\springframework\spring-webmvc\5.2.5.RELEASE\spring-webmvc-5.2.5.RELEASE.jar;D:\Tools\maven_repo\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Tools\maven_repo\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Tools\maven_repo\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Tools\maven_repo\mysql\mysql-connector-java\8.0.19\mysql-connector-java-8.0.19.jar;D:\Tools\maven_repo\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\Tools\maven_repo\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Tools\maven_repo\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2020.1.1\lib\idea_rt.jar" com.reims.main.nacos.NacosApplication
Connected to the target VM, address: '127.0.0.1:57519', transport: 'socket'


. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)


2020-05-01 12:22:08.223 INFO 2316 --- [ main] com.reims.main.nacos.NacosApplication : Starting NacosApplication on DESKTOP-MBI0KTI with PID 2316 (D:\ProjectWork\reims\nacos\target\classes started by wei in D:\ProjectWork\reims\nacos)
2020-05-01 12:22:08.227 INFO 2316 --- [ main] com.reims.main.nacos.NacosApplication : No active profile set, falling back to default profiles: default
2020-05-01 12:22:08.933 INFO 2316 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-05-01 12:22:08.940 INFO 2316 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-05-01 12:22:08.940 INFO 2316 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-05-01 12:22:08.997 INFO 2316 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-05-01 12:22:08.997 INFO 2316 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 729 ms
2020-05-01 12:22:09.116 INFO 2316 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-05-01 12:22:09.241 INFO 2316 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-05-01 12:22:09.243 INFO 2316 --- [ main] com.reims.main.nacos.NacosApplication : Started NacosApplication in 1.356 seconds (JVM running for 2.355)


添加nacos 相关jar包引用:

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.reims.main</groupId>
<artifactId>nacos</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacos</name>
<description>Demo project for Spring Boot</description>


<properties>
<java.version>1.8</java.version>
<nacos.version>1.2.1</nacos.version>
</properties>


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


<!-- Nacos Server 相关配置 -->


<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-config</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-core</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-naming</artifactId>
<version>${nacos.version}</version>
</dependency>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>


<!-- Nacos Server END -->


<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>


</project>


前端我们先直接复用,copy源码中static 目录 到resources 目录下

此时启动项目即可访问 http://localhost:8080,但是无法登录:


在application.properties 中添加端口和访问域:

# spring
server.servlet.context-path=/nacos
server.port=9000

实现功能

把源码中的代码实现复制到项目中(我尝试了最小构建,只复制某一个方法,但是发现nacos-config 依赖了很多东西,比较麻烦,所以干脆直接先复制过来。目前我们没有基于更深一层的源码构建,只能先这样了),
在修改启动类注解,增加nacos的扫描路径:
@SpringBootApplication(scanBasePackages=("com.reims.main,com.alibaba.nacos"))

启动参数中添加单机启动:

-Dnacos.standalone=true

此时启动项目,报错:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-05-01 23:06:43.745 ERROR 18756 --- [ main] o.s.boot.SpringApplication : Application run failed


org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configController' defined in URL [jar:file:/D:/Tools/maven_repo/com/alibaba/nacos/nacos-config/1.2.1/nacos-config-1.2.1.jar!/com/alibaba/nacos/config/server/controller/ConfigController.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configSubService' defined in URL [jar:file:/D:/Tools/maven_repo/com/alibaba/nacos/nacos-config/1.2.1/nacos-config-1.2.1.jar!/com/alibaba/nacos/config/server/service/ConfigSubService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverListService': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'useAddressServer' in value "${useAddressServer}"
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
at com.reims.main.nacos.NacosApplication.main(NacosApplication.java:11)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configSubService' defined in URL [jar:file:/D:/Tools/maven_repo/com/alibaba/nacos/nacos-config/1.2.1/nacos-config-1.2.1.jar!/com/alibaba/nacos/config/server/service/ConfigSubService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverListService': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'useAddressServer' in value "${useAddressServer}"
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1290)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1210)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 19 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverListService': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'useAddressServer' in value "${useAddressServer}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1290)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1210)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'useAddressServer' in value "${useAddressServer}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:178)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:236)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:909)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1231)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1210)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
... 45 common frames omitted


Disconnected from the target VM, address: '127.0.0.1:55865', transport: 'socket'


Process finished with exit code 1


在配置文件添加
useAddressServer=true
再次启动即可成功,访问:http://localhost:9000/nacos/

输入nacos/nacos 即可登录:

在配置文件添加数据库配置,登录成功后即可看到我们已有的配置:

登录流程详探

篇幅有限,今天先仅跟踪这一个流程。
实现登陆的类为:UserController.java 中的login方法:
 @PostMapping("/login")
public Object login(@RequestParam String username, @RequestParam String password,
                        HttpServletResponse response, HttpServletRequest request) throws AccessException {
if (AuthSystemTypes.NACOS.name().equalsIgnoreCase(authConfigs.getNacosAuthSystemType())) {
            NacosUser user = (NacosUser) authManager.login(request);
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER,
NacosAuthConfig.TOKEN_PREFIX + user.getToken());


JSONObject result = new JSONObject();
result.put(Constants.ACCESS_TOKEN, user.getToken());
result.put(Constants.TOKEN_TTL, authConfigs.getTokenValidityInSeconds());
result.put(Constants.GLOBAL_ADMIN, user.isGlobalAdmin());
return result;
        }
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 UsernamePasswordAuthenticationToken
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
RestResult<String> rr = new RestResult<String>();
try {
//通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
Authentication authentication = authenticationManager.authenticate(authenticationToken);
//将 Authentication 绑定到 SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
//生成Token
String token = jwtTokenUtils.createToken(authentication);
//将Token写入到Http头部
response.addHeader(NacosAuthConfig.AUTHORIZATION_HEADER, "Bearer " + token);
rr.setCode(200);
rr.setData("Bearer " + token);
return rr;
} catch (BadCredentialsException authentication) {
rr.setCode(401);
rr.setMessage("Login failed");
return rr;
}
}
可以看出,在if中判断了某个条件,形成了两种校验的方法。if中成立的条件是:配置文件中添加了以下两个参数,
nacos.core.auth.system.type=nacos
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789

目前发现的区别在于使用以上配置时,生成tocken时会使用这个key和userName进行计算生成Tocken:

/**
* Create token
*
* @param authentication auth info
* @return token
*/
public String createToken(Authentication authentication) {
return createToken(authentication.getName());
    }
public String createToken(String userName) {


long now = (new Date()).getTime();


Date validity;
validity = new Date(now + authConfigs.getTokenValidityInSeconds() * 1000L);


Claims claims = Jwts.claims().setSubject(userName);


return Jwts.builder()
.setClaims(claims)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, authConfigs.getSecretKey())
.compact();
}

未使用时,是直接生成的:

String token = jwtTokenUtils.createToken(authentication);
 /**
* Create token
*
* @param authentication auth info
* @return token
*/
public String createToken(Authentication authentication) {
/**
* Current time
*/
long now = (new Date()).getTime();
/**
* Validity date
*/
Date validity;
validity = new Date(now + this.tokenValidityInMilliseconds);


/**
* create token
*/
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, "")
.setExpiration(validity)
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}

举例:

使用nacos.core.auth.system.type使生成的tocken为:

不使用是生成tocken为:

可以看出,生成的方式明显是不一样的,第一种的更安全一些。

我们详细看下两种验证的方式:

第一种:

首先进入authManager.login 开始处理


通过resolveToken(req)来进行校验和生成Tocken


  @Override
public User login(Object request) throws AccessException {
HttpServletRequest req = (HttpServletRequest) request;
String token = resolveToken(req);
if (StringUtils.isBlank(token)) {
throw new AccessException("user not found!");
}


try {
tokenManager.validateToken(token);
} catch (ExpiredJwtException e) {
throw new AccessException("token expired!");
} catch (Exception e) {
throw new AccessException("token invalid!");
}


Authentication authentication = tokenManager.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);


String username = authentication.getName();
NacosUser user = new NacosUser();
user.setUserName(username);
user.setToken(token);
List<RoleInfo> roleInfoList = roleService.getRoles(username);
if (roleInfoList != null) {
for (RoleInfo roleInfo : roleInfoList) {
if (roleInfo.getRole().equals(NacosRoleServiceImpl.GLOBAL_ADMIN_ROLE)) {
user.setGlobalAdmin(true);
break;
}
}
}


return user;
}
/**
* Get token from header
*/
private String resolveToken(HttpServletRequest request) throws AccessException {
String bearerToken = request.getHeader(NacosAuthConfig.AUTHORIZATION_HEADER);
if (StringUtils.isNotBlank(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
}
bearerToken = request.getParameter(Constants.ACCESS_TOKEN);
if (StringUtils.isBlank(bearerToken)) {
String userName = request.getParameter("username");
String password = request.getParameter("password");
bearerToken = resolveTokenFromUser(userName, password);
}


return bearerToken;
}
    private String resolveTokenFromUser(String userName, String rawPassword) throws AccessException {


try {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, rawPassword);
authenticationManager.authenticate(authenticationToken);
} catch (AuthenticationException e) {
throw new AccessException("unknown user!");
}


return tokenManager.createToken(userName);
}
最终还是封装UsernamePasswordAuthenticationToken通过AuthenticationManager调用了WebSecurityConfigurerAda的authenticate方法(该方法调用AbstractUserDetailsAuthenticationProviderauthenticate)来调用了继承了UserDetails接口的NacosUserDetails(需实现loadUserByUsername方法来实现数据库的查询
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (delegate != null) {
      //调用AbstractUserDetailsAuthenticationProvider的authenticate
return delegate.authenticate(authentication);
}


synchronized (delegateMonitor) {
if (delegate == null) {
delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}


return delegate.authenticate(authentication);
}


AbstractUserDetailsAuthenticationProvider的authenticate

然后从userCache中获取继承了UserDetails的services,调用其loadUserByUsername方法实现数据库查询

public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
() -> messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));


// Determine username
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();


boolean cacheWasUsed = true;
    //这里从userCache中获取继承了UserDetails的services
UserDetails user = this.userCache.getUserFromCache(username);


if (user == null) {
cacheWasUsed = false;


try {
      //如果通过名称无法从this.userCache获取,这里进行处理,会进入DaoAuthenticationProvider的retrieveUser方法
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException notFound) {
logger.debug("User '" + username + "' not found");


if (hideUserNotFoundExceptions) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
else {
throw notFound;
}
}


Assert.notNull(user,
"retrieveUser returned null - a violation of the interface contract");
}


try {
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
else {
throw exception;
}
}


postAuthenticationChecks.check(user);


if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}


Object principalToReturn = user;


if (forcePrincipalAsString) {
principalToReturn = user.getUsername();
}


return createSuccessAuthentication(principalToReturn, authentication, user);
}


DaoAuthenticationProviderretrieveUser方法
 final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
    //通过这里来加载继承了UserDetails的services,然后调用loadUserByUsername来获取数据
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}



/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.reims.main.nacos.console.security.nacos.users;




import com.alibaba.nacos.config.server.auth.UserPersistService;
import com.alibaba.nacos.config.server.model.Page;
import com.alibaba.nacos.config.server.model.User;
import com.alibaba.nacos.core.auth.AuthConfigs;
import com.alibaba.nacos.core.utils.Loggers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
* Custem user service
*
* @author wfnuser
* @author nkorange
*/
@Service
public class NacosUserDetailsServiceImpl implements UserDetailsService {


private Map<String, User> userMap = new ConcurrentHashMap<>();


@Autowired
private UserPersistService userPersistService;


@Autowired
private AuthConfigs authConfigs;


@Scheduled(initialDelay = 5000, fixedDelay = 15000)
private void reload() {
try {
Page<User> users = getUsersFromDatabase(1, Integer.MAX_VALUE);
if (users == null) {
return;
}


Map<String, User> map = new ConcurrentHashMap<>(16);
for (User user : users.getPageItems()) {
map.put(user.getUsername(), user);
}
userMap = map;
} catch (Exception e) {
Loggers.AUTH.warn("[LOAD-USERS] load failed", e);
}
}


@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMap.get(username);
if (!authConfigs.isCachingEnabled()) {
user = userPersistService.findUserByUsername(username);
}


if (user == null) {
throw new UsernameNotFoundException(username);
}
return new NacosUserDetails(user);
}


public void updateUserPassword(String username, String password) {
userPersistService.updateUserPassword(username, password);
}


public Page<User> getUsersFromDatabase(int pageNo, int pageSize) {
return userPersistService.getUsers(pageNo, pageSize);
}


public User getUser(String username) {
User user = userMap.get(username);
if (!authConfigs.isCachingEnabled()) {
user = getUserFromDatabase(username);
}
return user;
}


public User getUserFromDatabase(String username) {
return userPersistService.findUserByUsername(username);
}


public void createUser(String username, String password) {
userPersistService.createUser(username, password);
}


public void deleteUser(String username) {
userPersistService.deleteUser(username);
}
}


到此,生成Tocken完成。在

NacosAuthManager

的login方法中,后续继续获取了角色相关的信息,有代码可知是直接差了数据库,就不在详细描述!


第二种:

第二种方式是直接根据用户名和密码,创建了一个UsernamePasswordAuthenticationToken,然后调用authenticate方法直接进入WebSecurityConfigurerAda的authenticate方法来进行验证,之后的用户名和密码调用数据库验证过程与第一种是一致的。



验证完成后,直接调用jwtTokenUtils.createToken(authentication)来生成Tocken

   public String createToken(Authentication authentication) {
/**
* Current time
*/
long now = (new Date()).getTime();
/**
* Validity date
*/
Date validity;
validity = new Date(now + this.tokenValidityInMilliseconds);


/**
* create token
*/
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, "")
.setExpiration(validity)
.signWith(secretKey, SignatureAlgorithm.HS256)
.compact();
}


        至此,本次登录过程分析完毕,虽然更深的原理没有详细分析,比如Spring security 的验证过程和原理,不过这些不在本篇中详细讲述了,更多问题留待以后具体分析。




如果觉得有用,请点下在看,关注公众号重塑之路,第一时间得到资源推送


文章转载自重塑之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论