java 如何在泽西岛验证用户
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42373642/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to authenticate users in Jersey
提问by Weaveon
I am writing a RESTful application in Java using Jersey, and i need to authenticate users. I know i can specify the roles in the resource using the annotations @RolesAllowed, but i can't understand how a user is associated to a specific role. The client sends username and password in this way
我正在使用 Jersey 在 Java 中编写 RESTful 应用程序,我需要对用户进行身份验证。我知道我可以使用 @RolesAllowed 注释指定资源中的角色,但我无法理解用户如何与特定角色相关联。客户端以这种方式发送用户名和密码
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(user, password);
Client client = ClientBuilder.newClient();
client.register(feature);
WebTarget target = client.target(baseUrl).path(urlString);
Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
Supposing that some methods can be used only by superusers and others by any user, how can i distinguish them when the username and the password are sent by the client?
假设某些方法只能由超级用户使用,而其他方法可以由任何用户使用,那么在客户端发送用户名和密码时如何区分它们?
回答by Paul Samsotha
I know i can specify the roles in the resource using the annotations @RolesAllowed, but i can't understand how a user is associated to a specific role
我知道我可以使用注释@RolesAllowed 指定资源中的角色,但我无法理解用户如何与特定角色相关联
The role information is stored in the DB. Assuming say you have a User
that models the USER and ROLES table in the DB
角色信息存储在 DB 中。假设你有一个User
对数据库中的 USER 和 ROLES 表进行建模
class User {
String username;
List<String> roles;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public List<String> getRoles() { return roles; }
public void setRoles(List<String> roles) { this.roles = roles; }
}
You would get the User
inside a Jersey filter. This is also where you would authenticate.
你会得到User
一个泽西过滤器的内部。这也是您进行身份验证的地方。
@Provider
@Priority(Priorities.AUTHENTICATION) // needs to happen before authorization
class AuthenticationFilter implements ContainerRequestFilter {
@Inject
private UserService userService; // this is your own service
@Override
public void filter(ContainerRequestFilter filter) {
// note, this is a lazy implementation of Basic auth.
// it doesn't do ant error checking. Please see
// link at bottom for better imlementation
String authzHeader = filter.getHeaderString(HttpHeaders.AUTHORIZATION); // (1)
String decoded = Base64.decodeAsString(authzHeader);
String[] split = decoded.split(":");
User user = userService.getUser(split[0]); // (2)
if (user == null || !user.getPassword().equals(someHash(split[1])) { // (3)
throw new UnauthorizedException();
}
SecurityContext oldContext = filter.getSecurityContext(); // (4)
filter.setSecurityContext(new BasicSecurityConext(user, oldContext.isSecure()));
}
}
What you're doing here is:
你在这里做的是:
- Parsing the Basic Auth Authorization header
- Getting the
User
with the username - Doing your authentication
- Setting a new
SecurityContext
.
- 解析 Basic Auth Authorization 标头
- 使用
User
用户名获取 - 进行身份验证
- 设置一个新的
SecurityContext
.
The BasicSecurityContext
is shown below. This is where you will associate roles with the user.
在BasicSecurityContext
下面示出。这是您将角色与用户关联的地方。
static class BasicSecurityContext implements SecurityContext {
private final User user;
private final boolean secure;
public BasicSecurityContext(User user, boolean secure) {
this.user = user;
this.secure = secure;
}
@Override
public Principal getUserPrincipal() {
return new Principal() {
@Override
public String getName() {
return user.getUsername();
}
};
}
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
@Override
public boolean isSecure() { return secure; }
@Override
public boolean isUserInRole(String role) {
return user.getRoles().contains(role);
}
}
If you look at the bottom at the isUserInRole
. What will happen is that Jersey will grab the @RolesAllowed
annotation from the resource method or class, grab the values, then pass them to the isUserInRole
. If it returns true
, then the user is authorized. In pseudo-code
如果您查看isUserInRole
. 将会发生的是 Jersey 将从@RolesAllowed
资源方法或类中获取注释,获取值,然后将它们传递给isUserInRole
. 如果返回true
,则用户已获得授权。在伪代码中
@RolesAllowed({"USER", "SUPER_USER"})
public Response get() {}
...
RolesAllowed annotation = resourceMethod.getAnnotation(RolesAllowed.class);
String roles = annotation.value();
SecurityContext context = getSecurityContext();
for (String role: roles) {
if (context.isUserInRole(role)) {
return;
}
}
throw new ForbiddenException();
This is just pseudo-code, but it shows how Jersey handles the authorizaiton, using the @RolesAllowed
, the SecurityContext
, and how you implement the isUserInRole
.
这仅仅是伪代码,但它表明新泽西州如何处理authorizaiton使用@RolesAllowed
的SecurityContext
,以及如何实现isUserInRole
。
This authorization feature is not automatically turned on. You need to turn it on yourself. To do so, simply register the RolesAllowedDynamicFeature
此授权功能不会自动开启。你需要自己打开它。为此,只需注册RolesAllowedDynamicFeature
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(RolesAllowedDynamicFeature.class);
}
}
One thing to note here is that in all of the above, we are implementing our basic authentication and setting of the security context. There is nothing really wrong with this. But if you are using the servlet container authentication mechanism, Jersey will actually take the auth information from the HttpServletRequest
. The HttpServletRequest
has a getUserPrincipal()
method and a isUserInRole
method. Jersey will use these to delegate in the SecurityContext
. So if you areuser the container authentication, then you don't really need to implement anything. You just need to register the RolesAllowedDynamicFeature
这里要注意的一件事是,在上述所有内容中,我们正在实施我们的基本身份验证和安全上下文设置。这并没有什么不妥。但是如果你使用 servlet 容器认证机制,Jersey 实际上会从HttpServletRequest
. 的HttpServletRequest
具有getUserPrincipal()
方法和isUserInRole
方法。Jersey 将使用这些在SecurityContext
. 因此,如果您是容器身份验证的用户,那么您实际上不需要执行任何操作。您只需要注册RolesAllowedDynamicFeature
If you want to use your container's authentication mechanism, you should consult your server's documentation. After having set up a realm in with your server, you will then need to configure the web.xml
with the security information. There's an example in the link below. You should also find this information in the Java EE docs under the web security section.
如果要使用容器的身份验证机制,则应查阅服务器的文档。在您的服务器中设置了一个域之后,您将需要web.xml
使用安全信息配置域。下面的链接中有一个例子。您还应该在 Web 安全部分下的 Java EE 文档中找到此信息。
See also:
也可以看看:
- Filter and Interceptorsto learn more about working with filters.
- Securityfor a little more info on working with security in Jersey.
- A better implementation of the basic auth filter
- 过滤器和拦截器以了解有关使用过滤器的更多信息。
- 安全上在泽西安全工作多一点信息。
- 基本身份验证过滤器的更好实现
回答by Saptarshi Basu
There are two things we need to address
我们需要解决两件事
- Authentication - Checking if the user is really the one it claims to be
- Authorization - If the authenticated user has the privilege to access the given method
- 身份验证 - 检查用户是否真的是它声称的那个人
- 授权 - 如果经过身份验证的用户有权访问给定的方法
To do both authentication and authorization, we need a data store that store that stores the following mapping:
为了同时进行身份验证和授权,我们需要一个数据存储来存储以下映射:
- Mapping between user and its password
- Mapping between roles and users
- Mapping between roles and permissions
- 用户及其密码之间的映射
- 角色和用户之间的映射
- 角色和权限的映射
Here the first mapping is required for authentication and the other two mappings are used for authorization.
这里需要第一个映射进行身份验证,其他两个映射用于授权。
Also, note that we need to do authentication and authorization for every API call. So we will be doing a lot of read operations.
另请注意,我们需要对每个 API 调用进行身份验证和授权。所以我们会做很多读操作。
Hence, usually a directory server or Ldap server such as Apache DS is used to store these mappings because a directory server is a read optimised data store.
因此,通常使用目录服务器或 Ldap 服务器(例如 Apache DS)来存储这些映射,因为目录服务器是读取优化的数据存储。
In a RESTful application usually a filter is used to extract the username and password from the request header, and do the authentication with the Ldap server. IF the authenication is successful, the next step is to extract the permissions of the user from the Ldap server by consulting the user-role and role-permission mappings. If the user is authorized, only in that case the control flows to the actual API business logic.
在 RESTful 应用程序中,通常使用过滤器从请求头中提取用户名和密码,并与 LDAP 服务器进行身份验证。如果认证成功,下一步就是通过查询user-role和role-permission映射从Ldap服务器中提取用户的权限。如果用户获得授权,只有在这种情况下,控制才会流向实际的 API 业务逻辑。
Refer to thisanswer for details.
有关详细信息,请参阅此答案。
回答by Luis Enrique Moreno Gutierrez
HttpAuthenticationFeature class provides HttpBasic and Digest client authentication capabilities. The feature work in one of 4 modes;
HttpAuthenticationFeature 类提供 HttpBasic 和 Digest 客户端身份验证功能。该功能以 4 种模式之一工作;
BASIC:It's preemptive authentication way i.e. information is send always with each HTTP request. This mode must be combined with usage of SSL/TLS as the password is send only BASE64 encoded.
BASIC:它是抢占式身份验证方式,即信息始终与每个 HTTP 请求一起发送。此模式必须与 SSL/TLS 结合使用,因为密码仅发送 BASE64 编码。
BASIC NON-PREEMPTIVE:It's non-preemptive authentication way i.e. auth information is added only when server refuses the request with 401 status code and then the request is repeated with authentication information.
BASIC NON-PREEMPTIVE:这是一种非抢占式认证方式,即只有当服务器拒绝带有401状态码的请求时才添加认证信息,然后使用认证信息重复该请求。
DIGEST:Http digest authentication. Does not require usage of SSL/TLS.
DIGEST:Http 摘要认证。不需要使用 SSL/TLS。
UNIVERSAL:Combination of basic and digest authentication in non-preemptive mode i.e. in case of 401 response, an appropriate authentication is used based on the authentication requested as defined in WWW-Authenticate HTTP header.
通用:非抢占模式下基本和摘要认证的组合,即在 401 响应的情况下,根据 WWW-Authenticate HTTP 标头中定义的请求认证使用适当的认证。
To use HttpAuthenticationFeature, build an instance of it and register with client. For example;
要使用 HttpAuthenticationFeature,请构建它的一个实例并向客户端注册。例如;
1) Basic authentication mode
1) 基本认证方式
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("username", "password");
final Client client = ClientBuilder.newClient();
client.register(feature);
2) Basic authentication : non-prempitive mode
2) 基本认证:非前置模式
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder()
.nonPreemptive()
.credentials("username", "password")
.build();
final Client client = ClientBuilder.newClient();
client.register(feature);
3) Universal mode
3) 通用模式
//Universal builder having different credentials for different schemes
HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder()
.credentialsForBasic("username1", "password1")
.credentials("username2", "password2").build();
final Client client = ClientBuilder.newClient();
client.register(feature);