Java RESTful 服务中的 httpcontext
httpcontext in Java RESTful service
我是 Java 的新手(来自 .Net 背景),我正在尝试使用 Jersey 框架编写 RESTful 服务。我提到了这个 link http://www.vogella.com/tutorials/REST/article.html
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
现在我想获取上面方法中的HTTPContext。基本上我希望获得登录的用户名,我希望应该可以使用 HttpContext.Request.
我正在使用 windows NT 身份验证。
谁能告诉我如何在 Java RESTful 服务中获取 HTTPContext/User 信息。
您可以使用以下代码在您的 REST 资源中获取 HTTPContext
。
public class RestResource {
@Context
private HttpServletRequest httpServletRequest;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
RequestContext requestContext = (RequestContext) httpServletRequest.getAttribute(RequestConstants.REQUEST_CONTEXT);
// Get whatever the value set in your request context
String userName = requestContext.requestContext.getQueryString("userName");
return userName;
}
}
如果您需要任何帮助,请告诉我。
简答
SecurityContext
injected with the @Context
注释允许您访问与安全相关的信息。它有一个请求范围并保存有关已通过身份验证的用户的详细信息。
对于可以用@Context
, check this answer注入的其他类型。
长答案
您必须注意 REST 身份验证必须是无状态的。也就是说,您不能依赖存储在服务器端的会话,因此必须在每个请求中发送用户凭据。也就是说,每个请求必须是authenticated/authorized。理解这个概念,这个answer可能有见地
请参阅下文如何使用 SecurityContext
两种方法来保护您的应用程序:
基本身份验证
RFC 7617 中描述的使用 HTTPS 的基本身份验证方案是保护 REST 应用程序的一种常用且有效的方法:
2. The 'Basic' Authentication Scheme
The Basic authentication scheme is based on the model that the client
needs to authenticate itself with a user-id and a password for each
protection space ("realm"). [...] The server will service the request only if it can validate
the user-id and password for the protection space applying to the
requested resource.
[...]
To receive authorization, the client
obtains the user-id and password from the user,
constructs the user-pass by concatenating the user-id, a single
colon (:
) character, and the password,
encodes the user-pass into an octet sequence,
and obtains the basic-credentials by encoding this octet sequence
using Base64 into a sequence of US-ASCII
characters.
[...]
If the user agent wishes to send the user-id "Aladdin" and password
"open sesame", it would use the following header field:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
[...]
您可以使用 ContainerRequestFilter
从 Authorization
请求中提取用户凭据 header,根据您的身份验证提供商对它们进行身份验证,然后使用请求的用户名设置 SecurityContext
。 AuthenticationService
实现由您决定:
@Provider
@Priority(Priorities.AUTHENTICATION)
class AuthenticationFilter implements ContainerRequestFilter {
@Inject
private AuthenticationService authenticationService;
@Override
public void filter(ContainerRequestFilter requestContext) {
// Get the HTTP Authorization header from the request
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Basic ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the Basic authentication token from the HTTP Authorization header
String token = authorizationHeader.substring("Basic".length()).trim();
// Decodes the token
String credentials = Base64.getDecoder().decode(token);
String[] split = decoded.split(":");
try {
// Authenticate the credentials against in your authentication provider
authenticationService.authenticate(split[0], split[1]);
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
// Updates the security context for the request
SecurityContext securityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(
new CustomSecurityContext(split[0], securityContext.isSecure()));
}
}
自定义 SecurityContext
实现可能是这样的:
public class CustomSecurityContext implements SecurityContext {
private final String username;
private final boolean secure;
public BasicSecurityContext(String username, boolean secure) {
this.username = username;
this.secure = secure;
}
@Override
public Principal getUserPrincipal() {
return new Principal() {
@Override
public String getName() {
return username;
}
};
}
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
@Override
public boolean isSecure() {
return secure;
}
@Override
public boolean isUserInRole(String role) {
return true;
}
}
然后可以使用 @Context
注释将 SecurityContext
注入到任何资源 class 中:
@Path("/example")
public class MyResource {
@Context
private SecurityContext securityContext;
...
}
也可以在资源方法参数中注入:
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myResourceMethod(@PathParam("id") Long id,
@Context SecurityContext securityContext) {
...
}
然后从SecurityContext
得到Principal
:
Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
基于令牌的身份验证
在基于令牌的身份验证方案中,令牌成为用户的凭据。
用户名和密码等硬凭证被交换为必须在每个请求中发送的令牌,然后服务器才能执行 authentication/authorization。令牌可以在短时间内有效,可以撤销,可以携带范围详细信息(令牌可以请求什么)等。
有关详细信息,请查看此答案。 SecurityContext
的使用方法与上述相同。
我是 Java 的新手(来自 .Net 背景),我正在尝试使用 Jersey 框架编写 RESTful 服务。我提到了这个 link http://www.vogella.com/tutorials/REST/article.html
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
现在我想获取上面方法中的HTTPContext。基本上我希望获得登录的用户名,我希望应该可以使用 HttpContext.Request.
我正在使用 windows NT 身份验证。
谁能告诉我如何在 Java RESTful 服务中获取 HTTPContext/User 信息。
您可以使用以下代码在您的 REST 资源中获取 HTTPContext
。
public class RestResource {
@Context
private HttpServletRequest httpServletRequest;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
RequestContext requestContext = (RequestContext) httpServletRequest.getAttribute(RequestConstants.REQUEST_CONTEXT);
// Get whatever the value set in your request context
String userName = requestContext.requestContext.getQueryString("userName");
return userName;
}
}
如果您需要任何帮助,请告诉我。
简答
SecurityContext
injected with the @Context
注释允许您访问与安全相关的信息。它有一个请求范围并保存有关已通过身份验证的用户的详细信息。
对于可以用@Context
, check this answer注入的其他类型。
长答案
您必须注意 REST 身份验证必须是无状态的。也就是说,您不能依赖存储在服务器端的会话,因此必须在每个请求中发送用户凭据。也就是说,每个请求必须是authenticated/authorized。理解这个概念,这个answer可能有见地
请参阅下文如何使用 SecurityContext
两种方法来保护您的应用程序:
基本身份验证
RFC 7617 中描述的使用 HTTPS 的基本身份验证方案是保护 REST 应用程序的一种常用且有效的方法:
2. The 'Basic' Authentication Scheme
The Basic authentication scheme is based on the model that the client needs to authenticate itself with a user-id and a password for each protection space ("realm"). [...] The server will service the request only if it can validate the user-id and password for the protection space applying to the requested resource.
[...]
To receive authorization, the client
obtains the user-id and password from the user,
constructs the user-pass by concatenating the user-id, a single colon (
:
) character, and the password,encodes the user-pass into an octet sequence,
and obtains the basic-credentials by encoding this octet sequence using Base64 into a sequence of US-ASCII characters.
[...]
If the user agent wishes to send the user-id "Aladdin" and password "open sesame", it would use the following header field:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
[...]
您可以使用 ContainerRequestFilter
从 Authorization
请求中提取用户凭据 header,根据您的身份验证提供商对它们进行身份验证,然后使用请求的用户名设置 SecurityContext
。 AuthenticationService
实现由您决定:
@Provider
@Priority(Priorities.AUTHENTICATION)
class AuthenticationFilter implements ContainerRequestFilter {
@Inject
private AuthenticationService authenticationService;
@Override
public void filter(ContainerRequestFilter requestContext) {
// Get the HTTP Authorization header from the request
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Basic ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the Basic authentication token from the HTTP Authorization header
String token = authorizationHeader.substring("Basic".length()).trim();
// Decodes the token
String credentials = Base64.getDecoder().decode(token);
String[] split = decoded.split(":");
try {
// Authenticate the credentials against in your authentication provider
authenticationService.authenticate(split[0], split[1]);
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
// Updates the security context for the request
SecurityContext securityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(
new CustomSecurityContext(split[0], securityContext.isSecure()));
}
}
自定义 SecurityContext
实现可能是这样的:
public class CustomSecurityContext implements SecurityContext {
private final String username;
private final boolean secure;
public BasicSecurityContext(String username, boolean secure) {
this.username = username;
this.secure = secure;
}
@Override
public Principal getUserPrincipal() {
return new Principal() {
@Override
public String getName() {
return username;
}
};
}
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
@Override
public boolean isSecure() {
return secure;
}
@Override
public boolean isUserInRole(String role) {
return true;
}
}
然后可以使用 @Context
注释将 SecurityContext
注入到任何资源 class 中:
@Path("/example")
public class MyResource {
@Context
private SecurityContext securityContext;
...
}
也可以在资源方法参数中注入:
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response myResourceMethod(@PathParam("id") Long id,
@Context SecurityContext securityContext) {
...
}
然后从SecurityContext
得到Principal
:
Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
基于令牌的身份验证
在基于令牌的身份验证方案中,令牌成为用户的凭据。
用户名和密码等硬凭证被交换为必须在每个请求中发送的令牌,然后服务器才能执行 authentication/authorization。令牌可以在短时间内有效,可以撤销,可以携带范围详细信息(令牌可以请求什么)等。
有关详细信息,请查看此答案。 SecurityContext
的使用方法与上述相同。