本指南向您展示如何使用Jakarta Authentication保护RESTful服务的端点。
在我们深入探讨如何保护RESTful Web服务之前,让我们首先定义一个我们将在整篇文章中使用的基本场景。
我们正在构建一个服务,该服务将在http://localhost:8080/jakartaee-hello-world/rest/hello接受HTTP GET请求。响应此请求时,我们的服务将返回如下例所示的JSON负载:
{
"hello": "world"
}
在本文中,我们将保护这个端点。
随着时间的推移,这个服务可以进一步增强和定制,以满足更复杂的需求和情况。通过从这个简单的设置开始,我们可以更好地了解使用Jakarta EE保护RESTful Web服务所涉及的基本步骤。现在,让我们继续这些步骤。对于那些不熟悉RESTful Web服务的读者,我们建议阅读我们之前的文章如何构建RESTful Web服务。
要安装JDK和Maven,我们可以使用SDKMan。我们可以按照如何操作指南中提到的步骤进行操作。
在本入门指南中,您可以使用Jakarta EE的Eclipse Starter,完成每个步骤,或者跳过您已经知道的基本设置阶段。您还可以从熟知的Maven原型开始使用IDE或选择项目结构。
要使用Jakarta EE的Eclipse Starter,我们需要采取以下步骤:
访问https://start.jakarta.ee。该服务将为应用程序设置所有必要的依赖项。当前版本的Starter仅支持Maven。将来,我们可能能够在Gradle和Maven之间做出选择。
从可用选项中选择所需的Jakarta EE版本。目前,选项包括Jakarta EE 8、Jakarta EE 9.1和Jakarta EE 10。此外,您可以选择Jakarta EE Platform或某种Jakarta EE Profile(Web、Core)。
对于此项目,我们选择了Jakarta EE 10 Platform、Java SE 11和WildFly作为运行时。
选择了所需的选项后,点击生成按钮。将生成项目结构和示例代码,以便可以构建和运行。
解压生成的代码,会得到一个应用程序的结构。可以在您喜欢的IDE中打开,然后直接在命令行运行。
.
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── java
│ └── org
│ └── eclipse
│ └── jakarta
│ └── hello
│ ├── Hello.java
│ └── HelloWorldResource.java
└── webapp
├── WEB-INF
│ └── web.xml
├── images
│ └── jakartaee_logo.jpg
└── index.html
生成的源代码包含一个嵌入式的Maven Wrapper。因此,请确保在Unix环境(Linux或Mac)下首先运行以下命令:
$ chmod +x mvnw
由于我们使用的是WildFly作为运行时,因此以下命令将运行应用程序:
$ ./mvnw clean package wildfly:run
另一方面,如果您已经安装了Maven,可以运行以下命令:
$ mvn clean package wildfly:run
对于Windows系统,不需要运行chmod
命令;而是应该使用mvnw.cmd
来代替mvnw
。
好了,在浏览器中访问以下URL,您将看到结果。
http://localhost:8080/jakartaee-hello-world
我们在之前的文章中已经介绍过关于测试的内容,此处不再赘述。
保护您的RESTful网络服务的第一步是对发出请求的用户进行身份验证。在Jakarta EE中,这可以通过使用Jakarta认证规范来实现。这个规范可以用来设置各种形式的认证,包括基本认证、基于表单的认证、摘要认证和基于令牌的认证。
下面谈谈基本认证。我们需要在web.xml文件中进行以下配置,对其进行设置:
<security-constraint>
<web-resource-collection>
<web-resource-name>受保护的REST服务</web-resource-name>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
<security-role>
<role-name>user</role-name>
</security-role>
这为应用程序设置了基本认证,并保护/rest
路径下的资源。
我们来详细分析一下:
安全约束(Security Constraint): 这用于定义特定一组网络资源的访问控制规则。
网络资源集合(Web Resource Collection): <web-resource-collection>
标签用于将URL分组,以便应用安全约束。在此配置中,网络资源集合被命名为“受保护的REST服务”,并适用于/rest
路径下的所有URL,由<url-pattern>/rest/*</url-pattern>
定义。
认证约束(Auth Constraint): <auth-constraint>
标签用于定义角色,这些角色 被允许访问网络资源集合中定义的URL。在本例中,只有具有'user'
角色的用户才被允许访问。
登录配置(Login Config): 这个标签用于定义服务器应使用的认证方法。在本例中,它被设置为BASIC,这意味着将使用HTTP基本认证。<realm-name>
被设置为ApplicationRealm
,这是服务器用于认证的领域。
安全角色(Security Role): 最后,<security-role>
标签用于定义可以在Web应用程序中使用的角色。在本例中,定义了“user”角色。请注意,在<role-name>
标签中使用的名称需要与服务器或应用程序安全设置中定义的角色匹配。
认证成功后,下一步是授权。授权是一个过程,用以确定经过认证的实体是否有权访问所需资源或执行特定操作。
Jakarta EE使用基于角色的访问控制(RBAC)进行授权。在RBAC中,访问决策基于系统中各个用户所拥有的角色。实体的角色会与应用程序部署描述符中定义的安全约束进行验证。
Jakarta EE提供了几个注解来保障应用程序的安全:
@RolesAllowed:
表示哪些安全角色被允许调用指定的方法。@DenyAll:
拒绝所有安全角色调用指定的方法。@PermitAll:
接受所有安全角色对指定方法的调用。@RunAs:
定义执行指定方法的安全身份。现在,让我们保护我们的端点。为此,我们只需在HelloWorldResource
中进行以下更改:
@Path("hello")
public class HelloWorldResource {
@GET
@RolesAllowed("user")
@Produces({ MediaType.APPLICATION_JSON })
public Hello hello(@QueryParam("name") String name) {
if ((name == null) || name.trim().isEmpty()) {
name = "world";
}
return new Hello(name);
}
}
在本文中,我们将使用@RolesAllowed
注解来保护这个端点。
运行应用程序,端点将会受到保护。我们试一下。
$ curl -v http://localhost:8080/jakartaee-hello-world/rest/hello
这将产生以下结果:
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /jakartaee-hello-world/rest/hello HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.87.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Expires: 0
< Connection: keep-alive
< WWW-Authenticate: Basic realm="ApplicationRealm"
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Type: text/html;charset=UTF-8
< Content-Length: 71
< Date: Wed, 05 Jul 2023 01:18:44 GMT
<
* Connection #0 to host localhost left intact
<html><head><title>Error</title></head><body>Unauthorized</body></html>
正如我们所见,我们的curl命令收到了一个401未授权的响应,因为它缺少请求资源所需的有效认证凭据。
下一步是创建一个凭据,以便我们可以安全地访问这个端点。
在WildFly中,application-users.properties
和application-roles.properties
文件通常位于您的WildFly安装目录下的standalone/configuration文件夹中。由于我们使用的是嵌入式WildFly,要找到这个文件夹,我们需要导航到target/server/standalone/configuration
文件夹。
application-users.properties
用于定义用户及其密码(哈希后的),而application-roles.properties
用于将用户映射到角色。
下面是如何操作的步骤:
/target/server/bin
)文件夹。add-user.sh
(对于Windows则是add-user.bat
)脚本来创建一个新用户。脚本将引导您完成添加新用户的过程:
$ ./add-user.sh
按照提示操作:
What type of user do you wish to add?
a) Management User (mgmt-users.properties)
b) Application User (application-users.properties)
(a): b
Enter the details of the new user to add.
Using realm 'ApplicationRealm' as discovered from the existing property files.
Username : your_username
Password : your_password
Re-enter Password : your_password
What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[ ]: your_role
请根据您的实际情况替换your_username
、your_password
和your_role
。在输入角色时,如果您想让用户属于多个角色,请使用逗号分隔每个角色的名称。如果您不想将用户分配到任何角色,请留空。
这些是在WildFly中定义用户和角色的简单步骤。请重新启动WildFly服务器以确保您的更改生效。
然而,我们这次必须确保在运行maven时不会清理target文件夹。因为maven的clean命令会完全删除target文件夹,我们创建的用户数据将会丢失。
因此,只需在不使用clean
的情况下运行:
./mvnw package wildfly:run
现在让我们再次运行curl命令:
curl --user username:password http://localhost:8080/jakartaee-hello-world/rest/hello
请将上述命令中的username和password替换为您在add-user.sh
提示中配置的用户名和密码。
如果用户名和密码正确,我们将得到以下输出。
{
"hello": "world"
}
恭喜!您刚刚学习了如何使用Jakarta Authentication来保护REST端点。虽然这是一个重要的起点,但现实世界的应用程序通常会将用户和角色数据存储在数据库或LDAP服务器中,而不是属性文件中。请注意,安全性是多方面的,包括HTTPS的使用和数据库保护等方面。为了获得全面的了解,建议参考官方的Jakarta EE安全文档,并探索其他相关在线资源。