Java 不同请求方式的httpServer基本认证
Java httpServer basic authentication for different request methods
我在 Java 中使用一个非常简单的 httpServer 来实现 api 休息与 GET、POST、PUT 和 DELETE。我正在使用基本身份验证,我有几个 classes Authentication.java 和 Authorisation.java,我用它们来验证和检查用户的权限。
所以,问题是我希望所有用户(经过身份验证)能够从我的 api 休息中获取信息,但只有具有特定权限的用户才能 POST,PUT并删除。那我该怎么做呢?
这就是我得到的
public class Server {
private static HttpServer server;
public static void start() throws IOException {
server = HttpServer.create(new InetSocketAddress(8000), 0);
HttpContext ctx = server.createContext("/users", new UserHandler());
ctx.setAuthenticator(new ApiRestBasicAuthentication("users"));
server.start();
}
}
这是我的 ApiRestBasicAuthentication
public class ApiRestBasicAuthentication extends BasicAuthenticator {
private UserAuthentication authentication = new UserAuthentication();
public ApiRestBasicAuthentication(String realm) {
super(realm);
}
@Override
public boolean checkCredentials(String user, String pwd) {
int authCode = authentication.authenticate(user, pwd);
return authCode == UserAuthentication.USER_AUTHENTICATED;
}
}
就像现在一样,检查凭据仅检查用户是否已通过身份验证。
但我想检查一下,如果方法是 POST、DELETE 或 PUT,我还应该检查具体的凭据。但是如何在我的 ApiRestBasicAuthentication 中获取该方法?我在我的处理程序中这样做 class
public void handle(HttpExchange httpExchange) throws IOException {
String method = httpExchange.getRequestMethod();
if ("post".equalsIgnoreCase(method)) {
createUser(httpExchange);
} else if ("get".equalsIgnoreCase(method)) {
readUsers(httpExchange);
} else if ("put".equalsIgnoreCase(method)) {
updateUser(httpExchange);
} else if ("delete".equalsIgnoreCase(method)) {
deleteUser(httpExchange);
}
}
也许这应该以其他方式完成。
有什么想法吗?
非常感谢。
可能有很多方法可以解决这个问题。这是我的建议之一:
创建一个用户对象,其中包含您想要的字段和一个名为 "role" 的字段。比方说只有 "admins" 可以发出除 "GET" 以外的 Http 请求,而 "regular" 用户只能发出 "GET"。有很多方法可以做到这一点,但一种方法是创建 "role" 字段 String 并使用 ENUM 为其赋值,这样以后更改起来很容易,并且只使用特定的术语。但你不必那样做。为您创建的字段和您以后可能需要的字段编写 get 和 set 方法,并且绝对是角色。
您需要确保包含句柄 (HttpExchange httpExchange) 的 class 能够看到当前登录的用户,并引用与他们关联的用户对象。然后你需要修改方法使得
if(loggedInUser.getRole().equals("admin")){
//allow whatever method
} else {
// allow "GET" or give some denied permission error
}
由于没有提供其他实现,我无法给出更详细的答案或确保这对您有用。
一个简单的方法是改变你的
ApiRestBasicAuthentication 喜欢:
public class ApiRestBasicAuthentication extends BasicAuthenticator {
private UserAuthentication authentication = new UserAuthentication();
public ApiRestBasicAuthentication(String realm) {
super(realm);
}
@Override
public Authenticator.Result authenticate(HttpExchange exch) {
Authenticator.Result result=super.authenticate(exch);
if(result instanceof Authenticator.Success) {
HttpPrincipal principal=((Authenticator.Success)result).getPrincipal();
String requestMethod=exch.getRequestMethod();
if( ADD SOME LOGIC HERE FOR PRINCIPAL AND REQUEST METHOD) {
return new return new Authenticator.Failure(401);
}
return result;
}
}
@Override
public boolean checkCredentials(String user, String pwd) {
int authCode = authentication.authenticate(user, pwd);
return authCode == UserAuthentication.USER_AUTHENTICATED;
}
}
并在其中为 requests/users 添加一些您想让身份验证器失败的逻辑。我在这里向您展示了如何在 authenticate 方法中获取该方法,但您需要指定凭据类型。
另一种解决方案是,如果您检查 BasicAuthenticator 的源代码,您可以看到它是如何实现 authenticate
方法的,并且您可以以类似的方式创建自己的实现,而不是扩展 BasicAuthenticator 并改用 get 方法只是用户名和密码。您可以查看源代码 here,我相信您一定能找到解决方法 ;)
通常在企业应用程序中,您可以使用一些外部安全管理系统 - 例如,如果您使用 Spring(当前 java 网络应用程序中的事实标准),您可以使用 spring security 并以更具声明性的方式执行此类安全模式和过滤器
我认为您应该创建一个 AuthenticationInterceptor
并绕过 GET
那里的请求,并相应地为其余 non-GET
请求应用身份验证机制。
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private ApiRestBasicAuthentication apiRestBasicAuthentication;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
switch (request.getMethod()) {
case "GET" :
// by-passing all GET requests
return true;
default :
return apiRestBasicAuthentication.checkCredentials(username, password);
}
}
}
虽然上述答案可能对您有效,但我认为您还应该考虑使用定义的角色和安全约束,它们可以在您的 web.xml 中定义,并使用 @RolesAllowed 注释在 REST 资源中使用。然后,这允许您单独或在 REST resource/class 级别专门允许方法的权限。
在 web.xml 中,它看起来像这样:-
<security-role>
<role-name>SERVERTOSERVER</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>REST API description</web-resource-name>
<url-pattern>/<path name>/*</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<description>Only allow users
from following roles</description>
<role-name>SERVERTOSERVER</role-name>
</auth-constraint>
</security-constraint>
以下链接有一些示例:Purpose of roles tags in tomcat-users.xml?,
https://www.thecoderscorner.com/team-blog/hosting-servers/17-setting-up-role-based-security-in-tomcat/
如果有帮助,这里是基于 Jersey 的应用程序的另一种解决方案:https://howtodoinjava.com/jersey/jersey-rest-security/
我在 Java 中使用一个非常简单的 httpServer 来实现 api 休息与 GET、POST、PUT 和 DELETE。我正在使用基本身份验证,我有几个 classes Authentication.java 和 Authorisation.java,我用它们来验证和检查用户的权限。
所以,问题是我希望所有用户(经过身份验证)能够从我的 api 休息中获取信息,但只有具有特定权限的用户才能 POST,PUT并删除。那我该怎么做呢?
这就是我得到的
public class Server {
private static HttpServer server;
public static void start() throws IOException {
server = HttpServer.create(new InetSocketAddress(8000), 0);
HttpContext ctx = server.createContext("/users", new UserHandler());
ctx.setAuthenticator(new ApiRestBasicAuthentication("users"));
server.start();
}
}
这是我的 ApiRestBasicAuthentication
public class ApiRestBasicAuthentication extends BasicAuthenticator {
private UserAuthentication authentication = new UserAuthentication();
public ApiRestBasicAuthentication(String realm) {
super(realm);
}
@Override
public boolean checkCredentials(String user, String pwd) {
int authCode = authentication.authenticate(user, pwd);
return authCode == UserAuthentication.USER_AUTHENTICATED;
}
}
就像现在一样,检查凭据仅检查用户是否已通过身份验证。 但我想检查一下,如果方法是 POST、DELETE 或 PUT,我还应该检查具体的凭据。但是如何在我的 ApiRestBasicAuthentication 中获取该方法?我在我的处理程序中这样做 class
public void handle(HttpExchange httpExchange) throws IOException {
String method = httpExchange.getRequestMethod();
if ("post".equalsIgnoreCase(method)) {
createUser(httpExchange);
} else if ("get".equalsIgnoreCase(method)) {
readUsers(httpExchange);
} else if ("put".equalsIgnoreCase(method)) {
updateUser(httpExchange);
} else if ("delete".equalsIgnoreCase(method)) {
deleteUser(httpExchange);
}
}
也许这应该以其他方式完成。 有什么想法吗?
非常感谢。
可能有很多方法可以解决这个问题。这是我的建议之一:
创建一个用户对象,其中包含您想要的字段和一个名为 "role" 的字段。比方说只有 "admins" 可以发出除 "GET" 以外的 Http 请求,而 "regular" 用户只能发出 "GET"。有很多方法可以做到这一点,但一种方法是创建 "role" 字段 String 并使用 ENUM 为其赋值,这样以后更改起来很容易,并且只使用特定的术语。但你不必那样做。为您创建的字段和您以后可能需要的字段编写 get 和 set 方法,并且绝对是角色。
您需要确保包含句柄 (HttpExchange httpExchange) 的 class 能够看到当前登录的用户,并引用与他们关联的用户对象。然后你需要修改方法使得
if(loggedInUser.getRole().equals("admin")){
//allow whatever method
} else {
// allow "GET" or give some denied permission error
}
由于没有提供其他实现,我无法给出更详细的答案或确保这对您有用。
一个简单的方法是改变你的 ApiRestBasicAuthentication 喜欢:
public class ApiRestBasicAuthentication extends BasicAuthenticator {
private UserAuthentication authentication = new UserAuthentication();
public ApiRestBasicAuthentication(String realm) {
super(realm);
}
@Override
public Authenticator.Result authenticate(HttpExchange exch) {
Authenticator.Result result=super.authenticate(exch);
if(result instanceof Authenticator.Success) {
HttpPrincipal principal=((Authenticator.Success)result).getPrincipal();
String requestMethod=exch.getRequestMethod();
if( ADD SOME LOGIC HERE FOR PRINCIPAL AND REQUEST METHOD) {
return new return new Authenticator.Failure(401);
}
return result;
}
}
@Override
public boolean checkCredentials(String user, String pwd) {
int authCode = authentication.authenticate(user, pwd);
return authCode == UserAuthentication.USER_AUTHENTICATED;
}
}
并在其中为 requests/users 添加一些您想让身份验证器失败的逻辑。我在这里向您展示了如何在 authenticate 方法中获取该方法,但您需要指定凭据类型。
另一种解决方案是,如果您检查 BasicAuthenticator 的源代码,您可以看到它是如何实现 authenticate
方法的,并且您可以以类似的方式创建自己的实现,而不是扩展 BasicAuthenticator 并改用 get 方法只是用户名和密码。您可以查看源代码 here,我相信您一定能找到解决方法 ;)
通常在企业应用程序中,您可以使用一些外部安全管理系统 - 例如,如果您使用 Spring(当前 java 网络应用程序中的事实标准),您可以使用 spring security 并以更具声明性的方式执行此类安全模式和过滤器
我认为您应该创建一个 AuthenticationInterceptor
并绕过 GET
那里的请求,并相应地为其余 non-GET
请求应用身份验证机制。
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private ApiRestBasicAuthentication apiRestBasicAuthentication;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
switch (request.getMethod()) {
case "GET" :
// by-passing all GET requests
return true;
default :
return apiRestBasicAuthentication.checkCredentials(username, password);
}
}
}
虽然上述答案可能对您有效,但我认为您还应该考虑使用定义的角色和安全约束,它们可以在您的 web.xml 中定义,并使用 @RolesAllowed 注释在 REST 资源中使用。然后,这允许您单独或在 REST resource/class 级别专门允许方法的权限。
在 web.xml 中,它看起来像这样:-
<security-role>
<role-name>SERVERTOSERVER</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>REST API description</web-resource-name>
<url-pattern>/<path name>/*</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<description>Only allow users
from following roles</description>
<role-name>SERVERTOSERVER</role-name>
</auth-constraint>
</security-constraint>
以下链接有一些示例:Purpose of roles tags in tomcat-users.xml?, https://www.thecoderscorner.com/team-blog/hosting-servers/17-setting-up-role-based-security-in-tomcat/
如果有帮助,这里是基于 Jersey 的应用程序的另一种解决方案:https://howtodoinjava.com/jersey/jersey-rest-security/