[英]Securing REST services in Jersey
我对网络服务非常陌生。 我已经使用 Jersey 2 与 Spring 集成公开了一些 REST 服务。 现在我需要使用用户名/密码进行身份验证来保护这些休息服务。 我被告知不要使用 Spring Security。
我不知道如何做到这一点。 我确实在网上搜索过,但各种链接显示了各种实现,我无法决定如何进行。
使用用户名和密码进行身份验证的常用方法是使用基本身份验证。 基本上客户端需要发送一个请求头Authorization
,头值为Basic Base64Encoded(username:password)
。 所以我的用户名是peeskillet
和我的密码是pass
,作为客户,我应该将标题设置为
Authorization: Basic cGVlc2tpbGxldDpwYXNz
在 servlet 环境中,容器应该支持基本身份验证。 您将在 web.xml 上配置此支持。 您可以在 Java EE 教程的48.2 保护 Web 应用程序中看到一个示例。 您还会在示例中注意到
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
那是为了 SSL 支持。 推荐用于基本身份验证。
如果您不想处理使用安全域和登录模块、领域等的麻烦,则需要自定义 servlet 支持,或者如果您只是不在 servlet 环境中,则实现基本身份验证在ContainerRequestFilter
中真的不是太难。
你可以在jersey/examples/https-clientserver-grizzly看到一个完整的例子,说明如何做到这一点。 您应该关注SecurityFilter
过滤器中的基本流程是这样的
获取Authorization
标头。 如果它不存在,则抛出AuthenticationException
。 在这种情况下, AuthenticationExceptionMapper
将发送标头"WWW-Authenticate", "Basic realm=\\"" + e.getRealm() + "\\"
,它是 Basic Auth 协议的一部分
获得标头后,我们对其进行解析以获取 Base64 编码的用户名:密码。 然后我们对其进行解码,然后拆分,然后将用户名和密码分开。 如果此过程中的任何一个失败,则再次抛出映射到 400 Bad Request 的WebApplicationException
。
检查用户名和密码。 示例源代码只检查用户名是否为user
密码是否为password
,但您需要在过滤器中使用某些服务来验证此信息。 如果其中任何一个失败,则抛出AuthenticationException
如果一切顺利, User
将从authenticate
方法创建,并注入到Authorizer
(这是一个SecurityContext
)。 在 JAX-RS 中, SecurityContext
通常用于授权。
对于授权,如果您想保护某些资源的某些区域,您可以为您的类或方法使用@RolesAllowed
批注。 Jersey 通过注册RolesAllowedDynamicFeature
支持此注释。
幕后发生的事情是将从请求中获取SecurityContext
。 通过我链接的示例,您可以看到Authorizer
,它有一个重写的方法isUserInRole
。 将调用此方法以检查@RolesAllowed({"ADMIN"})
。 因此,当您创建SecurityContext
,您应该确保在重写的方法中包含用户的角色。
对于测试,您可以简单地使用浏览器。 如果一切设置正确,当你尝试访问该资源,你应该看到(在Firefox)对话框中看到这个帖子。 如果你使用cURL ,你可以做
C:/>curl -v -u username:password http://localhost:8080/blah/resource
这将发出基本身份验证请求。 由于-v
开关,您应该看到所有涉及的标头。 如果您只想使用客户端 API 进行测试,您可以在此处查看如何设置它。 上述三种情况中的任何一种,都会为您完成Base64编码,因此您不必担心。
至于 SSL,您应该查看容器的文档以获取有关如何设置它的信息。
所以这真的是你想要实现的问题。 我的情况是让这个东西用移动设备和单页应用程序 JavaScript 运行。
基本上,您需要做的就是生成某种标头,该值将在您的客户端发出的每个连续请求中都需要。
所以你做了一个端点,你在其中等待带有用户/密码的帖子:
@Path("/login")
public class AuthenticationResource {
@POST
@Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
现在您需要保护需要该令牌的资源:
@Path("/payment")
@AuthorizedWithToken
public class Payments {
@GET
@Produces("application/json")
public Response sync() {
(...)
}
}
注意@AuthorizedWithToken
注释。 您可以使用特殊元注释@NameBinding
自行创建此注释
@NameBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorizedWithToken {}
现在对于实现标头检查的过滤器:
@AuthorizedWithToken
@Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
@Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
您可以创建任意数量的自己的注释来检查 http 请求中的各种内容并将它们混合。 但是,您需要注意优先级,但这实际上很容易找到。 此方法需要使用https
但这很明显。
安全性有两种主要形式:
保护 Spring 应用程序的标准方法是使用 Spring Security(以前称为 Acegi)。 知道为什么不允许您使用它会很有趣。
您可以使用基于容器的安全性,但我猜您对 spring 的使用也排除了该选项。 由于选择 Spring 通常是为了避免使用完整的 J2EE 容器(编辑:尽管正如下面其他人指出的,大多数普通 servlet 容器确实允许您实现各种基于容器的安全方法)
这实际上只为您提供了一种选择,那就是推出您自己的安全性。
您对 Jersey 的使用表明这可能是一个 REST 应用程序。 在这种情况下,您真的应该坚持使用标准的 HTTP 身份验证方法,这些方法以强度相反的顺序具有以下风格:
REST 应用程序通常应该是“无状态的”,这基本上排除了基于表单的身份验证(因为您需要使用 Session),而留给您的是 BASIC、Digest 和 Certificate。
你的下一个问题是,我在验证谁。 如果您可以期望根据他们请求的 URL 知道用户的用户名和密码(假设它是所有用户的一组凭据),那么 Digest 是最好的选择,因为从不发送密码,只有一个散列。 如果您不知道密码(因为您要求第三方系统对其进行验证等),那么您就无法使用 BASIC。 但是您可以通过使用 SSL 来增强 BASIC 的安全性,或者更好的是,将 BASIC 与客户端证书身份验证相结合。 事实上,基于 HTTPS 的 BASIC 身份验证是保护大多数 REST 应用程序的标准技术。
您可以轻松实现一个 Servlet 过滤器,它会查找身份验证标头并自行验证凭据。 有很多此类过滤器的示例,它是一个独立的类文件。 如果未找到凭据,过滤器将返回 401,并在响应标头中传递基本身份验证提示。 如果凭据无效,您将返回 403。应用程序安全本身几乎是整个职业,但我希望这会有所帮助。
正如前几篇文章所说,您可以采用不同的选项,实现不同的开销。 从实用的角度来看,如果您打算从这里开始并正在寻找一种简单实现的舒适方式,我建议使用 BASIC 身份验证的基于容器的选项。
如果使用tomcat,可以设置一个realm ,实现起来比较简单。 您可以使用 JDBCRealm,它从数据库中的指定列中获取用户和密码,并通过 server.xml 和 web.xml 对其进行配置。 每次您尝试访问您的应用程序时,这都会自动提示您输入凭据。 您没有任何应用程序端实现要做。
我现在可以告诉您的是,您已经完成了将 Jersey 与 Spring 集成的大部分“脏”工作。 我建议您使用基于应用程序的解决方案,它不会将您绑定到特定容器。 Spring Security 起初可能令人生畏,但是当您驯服野兽时,您会发现它实际上是一只友好的小狗。
事实是 Spring Security 是非常可定制的,只需实现它们的接口即可。 并且有很多文档和支持。 另外,您已经有一个基于 Spring 的应用程序。
由于您寻求的只是指导,我可以为您提供一些教程。 你可以利用这个博客。
http://www.baeldung.com/rest-with-spring-series/ http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security- 3-1-part-3/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.