1. Nexus私服概述

Nexus Repository Manager是Sonatype公司开发的企业级仓库管理工具,支持Maven、npm、Docker等多种包管理格式。在企业环境中,使用Nexus私服可以显著提升构建效率、保障依赖安全、实现统一管理。

1.1 核心优势

  1. 依赖缓存: 减少对外网依赖的访问,提升构建速度
  2. 安全控制: 统一管理依赖版本,避免安全漏洞
  3. 离线构建: 支持内网环境下的项目构建
  4. 版本管理: 统一管理企业内部组件版本
  5. CI/CD集成: 与持续集成工具无缝集成

1.2 技术架构

1
2
3
4
5
开发环境 → Maven/Gradle → Nexus私服 → 中央仓库
↓ ↓ ↓ ↓
本地构建 → 依赖下载 → 缓存管理 → 外部依赖
↓ ↓ ↓ ↓
CI/CD → 自动化构建 → 版本发布 → 生产部署

2. Nexus安装与配置

2.1 Docker安装Nexus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# docker-compose.yml
version: '3.8'
services:
nexus:
image: sonatype/nexus3:latest
container_name: nexus3
restart: unless-stopped
ports:
- "8081:8081" # Nexus Web界面端口
- "8082:8082" # Docker仓库端口
volumes:
- nexus-data:/nexus-data
- ./nexus.properties:/opt/sonatype/nexus/etc/nexus.properties
environment:
- INSTALL4J_ADD_VM_PARAMS=-Xms2g -Xmx2g -XX:MaxDirectMemorySize=3g
networks:
- nexus-network

volumes:
nexus-data:
driver: local

networks:
nexus-network:
driver: bridge

2.2 Nexus配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# nexus.properties
# Nexus基础配置
nexus.scripts.allowCreation=true
nexus.security.randompassword=false

# 数据库配置
nexus.datastore.enabled=true
nexus.datastore.nexus.h2.cacheSize=256

# 安全配置
nexus.security.userSource=default
nexus.security.realms=NexusAuthenticatingRealm,NexusAuthorizingRealm,DockerToken

# 性能优化
nexus.threads.corePoolSize=8
nexus.threads.maxPoolSize=200
nexus.threads.keepAliveTime=60

# 日志配置
nexus.log.level=INFO
nexus.log.appenders=console,file
nexus.log.file.path=/nexus-data/log/nexus.log

2.3 启动脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
# start-nexus.sh

echo "启动Nexus私服..."

# 检查Docker是否运行
if ! docker info > /dev/null 2>&1; then
echo "Docker未运行,请先启动Docker"
exit 1
fi

# 创建必要的目录
mkdir -p ./nexus-data/log
mkdir -p ./nexus-data/tmp
mkdir -p ./nexus-data/cache

# 设置目录权限
chmod 777 ./nexus-data

# 启动Nexus容器
docker-compose up -d

# 等待Nexus启动
echo "等待Nexus启动..."
sleep 30

# 检查Nexus状态
if curl -s http://localhost:8081/service/rest/v1/status > /dev/null; then
echo "Nexus启动成功!"
echo "访问地址: http://localhost:8081"
echo "默认用户名: admin"
echo "默认密码: admin123"
else
echo "Nexus启动失败,请检查日志"
docker-compose logs nexus
fi

3. Maven仓库配置

3.1 仓库类型说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Nexus仓库类型说明
*/
public class RepositoryTypes {

/**
* 仓库类型枚举
*/
public enum RepositoryType {
HOSTED("hosted", "托管仓库", "存储内部开发的组件"),
PROXY("proxy", "代理仓库", "代理外部仓库,如Maven中央仓库"),
GROUP("group", "组仓库", "组合多个仓库,提供统一访问入口");

private final String type;
private final String name;
private final String description;

RepositoryType(String type, String name, String description) {
this.type = type;
this.name = name;
this.description = description;
}

public String getType() { return type; }
public String getName() { return name; }
public String getDescription() { return description; }
}

/**
* 仓库策略枚举
*/
public enum ReleasePolicy {
RELEASE("Release", "发布版本", "存储稳定版本"),
SNAPSHOT("Snapshot", "快照版本", "存储开发版本"),
MIXED("Mixed", "混合版本", "同时存储发布和快照版本");

private final String policy;
private final String name;
private final String description;

ReleasePolicy(String policy, String name, String description) {
this.policy = policy;
this.name = name;
this.description = description;
}

public String getPolicy() { return policy; }
public String getName() { return name; }
public String getDescription() { return description; }
}
}

3.2 仓库创建配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* Maven仓库配置类
*/
@Component
public class MavenRepositoryConfig {

/**
* 创建Maven托管仓库配置
*/
public RepositoryConfig createHostedRepository(String name, String policy) {
return RepositoryConfig.builder()
.name(name)
.format("maven2")
.type("hosted")
.online(true)
.storage(StorageConfig.builder()
.blobStoreName("default")
.strictContentTypeValidation(true)
.writePolicy(policy) // RELEASE, SNAPSHOT, MIXED
.build())
.maven(MavenConfig.builder()
.versionPolicy(policy)
.layoutPolicy("STRICT")
.build())
.build();
}

/**
* 创建Maven代理仓库配置
*/
public RepositoryConfig createProxyRepository(String name, String remoteUrl) {
return RepositoryConfig.builder()
.name(name)
.format("maven2")
.type("proxy")
.online(true)
.storage(StorageConfig.builder()
.blobStoreName("default")
.strictContentTypeValidation(true)
.build())
.proxy(ProxyConfig.builder()
.remoteUrl(remoteUrl)
.contentMaxAge(1440) // 24小时
.metadataMaxAge(1440)
.build())
.negativeCache(NegativeCacheConfig.builder()
.enabled(true)
.timeToLive(1440)
.build())
.httpClient(HttpClientConfig.builder()
.blocked(false)
.autoBlock(true)
.connection(ConnectionConfig.builder()
.retries(3)
.userAgentSuffix("Nexus-Proxy")
.timeout(60)
.build())
.build())
.maven(MavenConfig.builder()
.versionPolicy("RELEASE")
.layoutPolicy("STRICT")
.build())
.build();
}

/**
* 创建Maven组仓库配置
*/
public RepositoryConfig createGroupRepository(String name, List<String> memberRepositories) {
return RepositoryConfig.builder()
.name(name)
.format("maven2")
.type("group")
.online(true)
.storage(StorageConfig.builder()
.blobStoreName("default")
.strictContentTypeValidation(true)
.build())
.group(GroupConfig.builder()
.memberNames(memberRepositories)
.build())
.build();
}
}

/**
* 仓库配置实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RepositoryConfig {
private String name;
private String format;
private String type;
private boolean online;
private StorageConfig storage;
private ProxyConfig proxy;
private NegativeCacheConfig negativeCache;
private HttpClientConfig httpClient;
private MavenConfig maven;
private GroupConfig group;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StorageConfig {
private String blobStoreName;
private boolean strictContentTypeValidation;
private String writePolicy;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ProxyConfig {
private String remoteUrl;
private int contentMaxAge;
private int metadataMaxAge;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NegativeCacheConfig {
private boolean enabled;
private int timeToLive;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class HttpClientConfig {
private boolean blocked;
private boolean autoBlock;
private ConnectionConfig connection;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ConnectionConfig {
private int retries;
private String userAgentSuffix;
private int timeout;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MavenConfig {
private String versionPolicy;
private String layoutPolicy;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupConfig {
private List<String> memberNames;
}

3.3 仓库管理服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
@Service
public class RepositoryManagementService {

@Autowired
private NexusApiClient nexusApiClient;

/**
* 创建Maven仓库
*/
public void createMavenRepositories() {
try {
// 1. 创建Maven中央仓库代理
RepositoryConfig mavenCentral = createProxyRepository(
"maven-central",
"https://repo1.maven.org/maven2/"
);
nexusApiClient.createRepository(mavenCentral);

// 2. 创建Spring仓库代理
RepositoryConfig springReleases = createProxyRepository(
"spring-releases",
"https://repo.spring.io/release/"
);
nexusApiClient.createRepository(springReleases);

// 3. 创建Spring快照仓库代理
RepositoryConfig springSnapshots = createProxyRepository(
"spring-snapshots",
"https://repo.spring.io/snapshot/"
);
nexusApiClient.createRepository(springSnapshots);

// 4. 创建内部发布仓库
RepositoryConfig internalReleases = createHostedRepository(
"internal-releases",
"RELEASE"
);
nexusApiClient.createRepository(internalReleases);

// 5. 创建内部快照仓库
RepositoryConfig internalSnapshots = createHostedRepository(
"internal-snapshots",
"SNAPSHOT"
);
nexusApiClient.createRepository(internalSnapshots);

// 6. 创建Maven公共组仓库
List<String> groupMembers = Arrays.asList(
"maven-central",
"spring-releases",
"spring-snapshots",
"internal-releases",
"internal-snapshots"
);
RepositoryConfig mavenPublic = createGroupRepository(
"maven-public",
groupMembers
);
nexusApiClient.createRepository(mavenPublic);

System.out.println("Maven仓库创建完成");

} catch (Exception e) {
System.err.println("创建Maven仓库失败: " + e.getMessage());
e.printStackTrace();
}
}

/**
* 创建代理仓库
*/
private RepositoryConfig createProxyRepository(String name, String remoteUrl) {
return RepositoryConfig.builder()
.name(name)
.format("maven2")
.type("proxy")
.online(true)
.storage(StorageConfig.builder()
.blobStoreName("default")
.strictContentTypeValidation(true)
.build())
.proxy(ProxyConfig.builder()
.remoteUrl(remoteUrl)
.contentMaxAge(1440)
.metadataMaxAge(1440)
.build())
.negativeCache(NegativeCacheConfig.builder()
.enabled(true)
.timeToLive(1440)
.build())
.httpClient(HttpClientConfig.builder()
.blocked(false)
.autoBlock(true)
.connection(ConnectionConfig.builder()
.retries(3)
.userAgentSuffix("Nexus-Proxy")
.timeout(60)
.build())
.build())
.maven(MavenConfig.builder()
.versionPolicy("RELEASE")
.layoutPolicy("STRICT")
.build())
.build();
}

/**
* 创建托管仓库
*/
private RepositoryConfig createHostedRepository(String name, String policy) {
return RepositoryConfig.builder()
.name(name)
.format("maven2")
.type("hosted")
.online(true)
.storage(StorageConfig.builder()
.blobStoreName("default")
.strictContentTypeValidation(true)
.writePolicy(policy)
.build())
.maven(MavenConfig.builder()
.versionPolicy(policy)
.layoutPolicy("STRICT")
.build())
.build();
}

/**
* 创建组仓库
*/
private RepositoryConfig createGroupRepository(String name, List<String> memberRepositories) {
return RepositoryConfig.builder()
.name(name)
.format("maven2")
.type("group")
.online(true)
.storage(StorageConfig.builder()
.blobStoreName("default")
.strictContentTypeValidation(true)
.build())
.group(GroupConfig.builder()
.memberNames(memberRepositories)
.build())
.build();
}
}

4. Maven客户端配置

4.1 settings.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<!-- settings.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">

<!-- 本地仓库路径 -->
<localRepository>${user.home}/.m2/repository</localRepository>

<!-- 是否与用户交互 -->
<interactiveMode>true</interactiveMode>

<!-- 是否离线模式 -->
<offline>false</offline>

<!-- 插件组 -->
<pluginGroups>
<pluginGroup>org.apache.maven.plugins</pluginGroup>
<pluginGroup>org.codehaus.mojo</pluginGroup>
</pluginGroups>

<!-- 代理配置 -->
<proxies>
<!-- 如果需要通过代理访问外网,可以配置代理 -->
<!--
<proxy>
<id>optional</id>
<active>true</active>
<protocol>http</protocol>
<username>proxyuser</username>
<password>proxypass</password>
<host>proxy.host.net</host>
<port>80</port>
<nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>
-->
</proxies>

<!-- 服务器认证信息 -->
<servers>
<!-- Nexus私服认证 -->
<server>
<id>nexus-releases</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>admin</username>
<password>admin123</password>
</server>
<server>
<id>nexus-public</id>
<username>admin</username>
<password>admin123</password>
</server>
</servers>

<!-- 镜像配置 -->
<mirrors>
<!-- 使用Nexus私服作为Maven中央仓库的镜像 -->
<mirror>
<id>nexus-public</id>
<mirrorOf>central</mirrorOf>
<name>Nexus Public Repository</name>
<url>http://localhost:8081/repository/maven-public/</url>
</mirror>
</mirrors>

<!-- 配置文件 -->
<profiles>
<!-- 开发环境配置 -->
<profile>
<id>development</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>nexus-public</id>
<name>Nexus Public Repository</name>
<url>http://localhost:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>nexus-public</id>
<name>Nexus Public Repository</name>
<url>http://localhost:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>

<!-- 生产环境配置 -->
<profile>
<id>production</id>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>nexus-releases</id>
<name>Nexus Releases Repository</name>
<url>http://localhost:8081/repository/internal-releases/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>never</updatePolicy>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</profile>
</profiles>

<!-- 激活的配置文件 -->
<activeProfiles>
<activeProfile>development</activeProfile>
</activeProfiles>
</settings>

4.2 pom.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<!-- 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">

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>nexus-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Nexus Demo Project</name>
<description>Nexus私服演示项目</description>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.0</spring-boot.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- Maven Compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>

<!-- Maven Deploy Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>

<!-- 分发管理 -->
<distributionManagement>
<!-- 发布版本仓库 -->
<repository>
<id>nexus-releases</id>
<name>Nexus Releases Repository</name>
<url>http://localhost:8081/repository/internal-releases/</url>
</repository>

<!-- 快照版本仓库 -->
<snapshotRepository>
<id>nexus-snapshots</id>
<name>Nexus Snapshots Repository</name>
<url>http://localhost:8081/repository/internal-snapshots/</url>
</snapshotRepository>
</distributionManagement>

<!-- 仓库配置 -->
<repositories>
<repository>
<id>nexus-public</id>
<name>Nexus Public Repository</name>
<url>http://localhost:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>

<!-- 插件仓库配置 -->
<pluginRepositories>
<pluginRepository>
<id>nexus-public</id>
<name>Nexus Public Repository</name>
<url>http://localhost:8081/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
<updatePolicy>daily</updatePolicy>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>

5. 权限管理与安全

5.1 用户和角色管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Nexus用户管理服务
*/
@Service
public class NexusUserManagementService {

@Autowired
private NexusApiClient nexusApiClient;

/**
* 创建用户
*/
public void createUsers() {
try {
// 1. 创建开发者用户
UserConfig developer = UserConfig.builder()
.userId("developer")
.firstName("Developer")
.lastName("User")
.emailAddress("developer@example.com")
.password("developer123")
.status("active")
.roles(Arrays.asList("nx-developer"))
.build();
nexusApiClient.createUser(developer);

// 2. 创建部署用户
UserConfig deployer = UserConfig.builder()
.userId("deployer")
.firstName("Deployer")
.lastName("User")
.emailAddress("deployer@example.com")
.password("deployer123")
.status("active")
.roles(Arrays.asList("nx-deployment"))
.build();
nexusApiClient.createUser(deployer);

// 3. 创建只读用户
UserConfig reader = UserConfig.builder()
.userId("reader")
.firstName("Reader")
.lastName("User")
.emailAddress("reader@example.com")
.password("reader123")
.status("active")
.roles(Arrays.asList("nx-readonly"))
.build();
nexusApiClient.createUser(reader);

System.out.println("用户创建完成");

} catch (Exception e) {
System.err.println("创建用户失败: " + e.getMessage());
e.printStackTrace();
}
}

/**
* 创建角色
*/
public void createRoles() {
try {
// 1. 创建开发者角色
RoleConfig developerRole = RoleConfig.builder()
.id("nx-developer")
.name("Developer")
.description("开发者角色,可以下载和上传快照版本")
.privileges(Arrays.asList(
"nx-repository-view-maven2-*-read",
"nx-repository-view-maven2-*-browse",
"nx-repository-view-maven2-internal-snapshots-read",
"nx-repository-view-maven2-internal-snapshots-browse",
"nx-repository-view-maven2-internal-snapshots-add",
"nx-repository-view-maven2-internal-snapshots-edit"
))
.roles(Arrays.asList("nx-anonymous"))
.build();
nexusApiClient.createRole(developerRole);

// 2. 创建部署角色
RoleConfig deployerRole = RoleConfig.builder()
.id("nx-deployment")
.name("Deployment")
.description("部署角色,可以上传发布版本")
.privileges(Arrays.asList(
"nx-repository-view-maven2-*-read",
"nx-repository-view-maven2-*-browse",
"nx-repository-view-maven2-internal-releases-read",
"nx-repository-view-maven2-internal-releases-browse",
"nx-repository-view-maven2-internal-releases-add",
"nx-repository-view-maven2-internal-releases-edit"
))
.roles(Arrays.asList("nx-anonymous"))
.build();
nexusApiClient.createRole(deployerRole);

// 3. 创建只读角色
RoleConfig readonlyRole = RoleConfig.builder()
.id("nx-readonly")
.name("Read Only")
.description("只读角色,只能查看和下载")
.privileges(Arrays.asList(
"nx-repository-view-maven2-*-read",
"nx-repository-view-maven2-*-browse"
))
.roles(Arrays.asList("nx-anonymous"))
.build();
nexusApiClient.createRole(readonlyRole);

System.out.println("角色创建完成");

} catch (Exception e) {
System.err.println("创建角色失败: " + e.getMessage());
e.printStackTrace();
}
}
}

/**
* 用户配置实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserConfig {
private String userId;
private String firstName;
private String lastName;
private String emailAddress;
private String password;
private String status;
private List<String> roles;
}

/**
* 角色配置实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RoleConfig {
private String id;
private String name;
private String description;
private List<String> privileges;
private List<String> roles;
}

5.2 安全策略配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* Nexus安全策略配置
*/
@Service
public class NexusSecurityConfigService {

@Autowired
private NexusApiClient nexusApiClient;

/**
* 配置安全策略
*/
public void configureSecurityPolicies() {
try {
// 1. 配置内容选择器
createContentSelectors();

// 2. 配置权限
createPrivileges();

// 3. 配置安全策略
createSecurityPolicies();

System.out.println("安全策略配置完成");

} catch (Exception e) {
System.err.println("配置安全策略失败: " + e.getMessage());
e.printStackTrace();
}
}

/**
* 创建内容选择器
*/
private void createContentSelectors() {
// 创建内部组件选择器
ContentSelectorConfig internalSelector = ContentSelectorConfig.builder()
.name("internal-components")
.description("内部组件选择器")
.expression("format == 'maven2' and path =~ '^/com/example/.*'")
.build();
nexusApiClient.createContentSelector(internalSelector);

// 创建第三方组件选择器
ContentSelectorConfig thirdPartySelector = ContentSelectorConfig.builder()
.name("third-party-components")
.description("第三方组件选择器")
.expression("format == 'maven2' and path =~ '^/(org|com|net)/.*'")
.build();
nexusApiClient.createContentSelector(thirdPartySelector);
}

/**
* 创建权限
*/
private void createPrivileges() {
// 创建内部组件权限
PrivilegeConfig internalPrivilege = PrivilegeConfig.builder()
.name("nx-repository-view-maven2-internal-*")
.description("内部组件权限")
.type("application")
.properties(Map.of(
"contentSelector", "internal-components",
"repository", "maven-public",
"actions", "browse,read"
))
.build();
nexusApiClient.createPrivilege(internalPrivilege);

// 创建第三方组件权限
PrivilegeConfig thirdPartyPrivilege = PrivilegeConfig.builder()
.name("nx-repository-view-maven2-third-party-*")
.description("第三方组件权限")
.type("application")
.properties(Map.of(
"contentSelector", "third-party-components",
"repository", "maven-public",
"actions", "browse,read"
))
.build();
nexusApiClient.createPrivilege(thirdPartyPrivilege);
}

/**
* 创建安全策略
*/
private void createSecurityPolicies() {
// 创建内部组件安全策略
SecurityPolicyConfig internalPolicy = SecurityPolicyConfig.builder()
.name("internal-components-policy")
.description("内部组件安全策略")
.contentSelectors(Arrays.asList("internal-components"))
.actions(Arrays.asList("browse", "read", "add", "edit"))
.build();
nexusApiClient.createSecurityPolicy(internalPolicy);

// 创建第三方组件安全策略
SecurityPolicyConfig thirdPartyPolicy = SecurityPolicyConfig.builder()
.name("third-party-components-policy")
.description("第三方组件安全策略")
.contentSelectors(Arrays.asList("third-party-components"))
.actions(Arrays.asList("browse", "read"))
.build();
nexusApiClient.createSecurityPolicy(thirdPartyPolicy);
}
}

/**
* 内容选择器配置
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ContentSelectorConfig {
private String name;
private String description;
private String expression;
}

/**
* 权限配置
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PrivilegeConfig {
private String name;
private String description;
private String type;
private Map<String, String> properties;
}

/**
* 安全策略配置
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SecurityPolicyConfig {
private String name;
private String description;
private List<String> contentSelectors;
private List<String> actions;
}

6. CI/CD集成

6.1 Jenkins集成配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Jenkinsfile
pipeline {
agent any

environment {
NEXUS_URL = 'http://localhost:8081'
NEXUS_REPOSITORY = 'internal-releases'
NEXUS_CREDENTIALS = 'nexus-credentials'
}

stages {
stage('Checkout') {
steps {
checkout scm
}
}

stage('Build') {
steps {
sh 'mvn clean compile'
}
}

stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}

stage('Package') {
steps {
sh 'mvn package -DskipTests'
}
}

stage('Deploy to Nexus') {
steps {
script {
if (env.BRANCH_NAME == 'main') {
// 发布版本
sh 'mvn deploy -DskipTests'
} else {
// 快照版本
sh 'mvn deploy -DskipTests -Dmaven.test.skip=true'
}
}
}
}

stage('Docker Build') {
steps {
script {
def image = docker.build("nexus-demo:${env.BUILD_NUMBER}")
docker.withRegistry('http://localhost:8082', 'nexus-docker-credentials') {
image.push()
image.push('latest')
}
}
}
}
}

post {
always {
cleanWs()
}
success {
echo 'Pipeline executed successfully'
}
failure {
echo 'Pipeline failed'
}
}
}

6.2 GitHub Actions集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'

- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2

- name: Build with Maven
run: mvn clean compile

- name: Run tests
run: mvn test

- name: Package
run: mvn package -DskipTests

- name: Deploy to Nexus
if: github.ref == 'refs/heads/main'
run: mvn deploy -DskipTests
env:
NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }}
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}

- name: Deploy Snapshot to Nexus
if: github.ref == 'refs/heads/develop'
run: mvn deploy -DskipTests -Dmaven.test.skip=true
env:
NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }}
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}

6.3 Maven插件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!-- pom.xml 中的插件配置 -->
<build>
<plugins>
<!-- Maven Deploy Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>false</skip>
</configuration>
</plugin>

<!-- Maven Release Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<tagNameFormat>v@{project.version}</tagNameFormat>
<releaseProfiles>release</releaseProfiles>
<goals>deploy</goals>
</configuration>
</plugin>

<!-- Maven Source Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- Maven Javadoc Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

7. 监控和维护

7.1 健康检查脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/bash
# nexus-health-check.sh

NEXUS_URL="http://localhost:8081"
ADMIN_USER="admin"
ADMIN_PASS="admin123"

echo "开始Nexus健康检查..."

# 1. 检查Nexus服务状态
echo "检查Nexus服务状态..."
if curl -s -f "$NEXUS_URL/service/rest/v1/status" > /dev/null; then
echo "✓ Nexus服务运行正常"
else
echo "✗ Nexus服务异常"
exit 1
fi

# 2. 检查仓库状态
echo "检查仓库状态..."
repositories=("maven-central" "spring-releases" "spring-snapshots" "internal-releases" "internal-snapshots" "maven-public")

for repo in "${repositories[@]}"; do
if curl -s -f "$NEXUS_URL/service/rest/v1/repositories/$repo" > /dev/null; then
echo "✓ 仓库 $repo 正常"
else
echo "✗ 仓库 $repo 异常"
fi
done

# 3. 检查存储空间
echo "检查存储空间..."
disk_usage=$(df -h /var/lib/docker/volumes/nexus_nexus-data | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$disk_usage" -lt 80 ]; then
echo "✓ 存储空间充足: ${disk_usage}%"
else
echo "⚠ 存储空间不足: ${disk_usage}%"
fi

# 4. 检查日志错误
echo "检查日志错误..."
error_count=$(docker logs nexus3 2>&1 | grep -i error | wc -l)
if [ "$error_count" -eq 0 ]; then
echo "✓ 无错误日志"
else
echo "⚠ 发现 $error_count 个错误日志"
fi

echo "Nexus健康检查完成"

7.2 备份脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# nexus-backup.sh

BACKUP_DIR="/backup/nexus"
NEXUS_DATA_DIR="/var/lib/docker/volumes/nexus_nexus-data"
DATE=$(date +%Y%m%d_%H%M%S)

echo "开始Nexus备份..."

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 1. 备份Nexus数据
echo "备份Nexus数据..."
docker run --rm -v nexus_nexus-data:/data -v "$BACKUP_DIR":/backup alpine tar czf "/backup/nexus-data-$DATE.tar.gz" -C /data .

# 2. 备份配置文件
echo "备份配置文件..."
cp -r ./nexus-data/etc "$BACKUP_DIR/nexus-config-$DATE"

# 3. 清理旧备份(保留7天)
echo "清理旧备份..."
find "$BACKUP_DIR" -name "nexus-data-*.tar.gz" -mtime +7 -delete
find "$BACKUP_DIR" -name "nexus-config-*" -mtime +7 -exec rm -rf {} \;

echo "Nexus备份完成: $BACKUP_DIR/nexus-data-$DATE.tar.gz"

7.3 监控配置

1
2
3
4
5
6
7
8
9
10
# prometheus.yml
global:
scrape_interval: 15s

scrape_configs:
- job_name: 'nexus'
static_configs:
- targets: ['localhost:8081']
metrics_path: '/service/rest/v1/status'
scrape_interval: 30s

8. 总结

通过Nexus私服的搭建和配置,我们成功构建了一个企业级的Maven仓库管理系统。关键特性包括:

8.1 核心优势

  1. 依赖管理: 统一管理内部和外部依赖
  2. 安全控制: 完善的用户权限和访问控制
  3. 性能提升: 本地缓存减少网络依赖
  4. CI/CD集成: 与持续集成工具无缝集成
  5. 监控维护: 完善的监控和备份机制

8.2 最佳实践

  1. 仓库规划: 合理规划仓库类型和策略
  2. 权限管理: 基于角色的权限控制
  3. 安全策略: 内容选择器和安全策略
  4. 自动化: CI/CD自动化部署
  5. 监控维护: 定期健康检查和备份

这套Nexus私服方案不仅能够满足企业级Maven仓库管理的需求,还为其他包管理格式提供了扩展基础,是现代Java企业开发的重要基础设施。