- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我正在开发的网络应用程序由一些 servlet 和 JAX-RS 网络服务组成。到目前为止,我一直使用 ContainerRequestFilter 来验证 REST 方法调用,但现在我还需要保护 servlet,所以我决定使用 web.xml 来定义安全约束。我的 web.xml 如下所示:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<security-constraint>
<web-resource-collection>
<web-resource-name>rest</web-resource-name>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>protected</web-resource-name>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>user</role-name>
</security-role>
<!-- Configure login to be HTTP Basic -->
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Restricted Zone</realm-name>
</login-config>
</web-app>
如果我正确理解 web.xml 的语法,那么我定义的意思是,就登录模块而言,对/rest/*(我所有的 JAX-RS 方法所在的位置)的访问是不受限制的,并且所有对/protected/* 路径(我保存安全 servlet 的地方)需要基本授权。
当我尝试打开其中一个安全 servlet 时,例如/protected/test,我在浏览器中获得了基本的身份验证登录对话框并且行为是正确的 - 如果我输入“管理员”用户的凭据,我将被允许访问。否则,我会收到一条“禁止”消息。
此外,当我尝试访问/rest/路径上的任何内容时,我没有看到基本的身份验证对话框,这正是我所期望的。但是,我在 ContainerRequestFilter 中获得的 Authorization header 不是我在 REST 请求中发送的 header ,而是我之前用于进入/protected/servlet 的 header 。
下面是拼图的其他部分:
standalone.xml(安全域部分)
<security-domain name="PaloSecurityDomain" cache-type="default">
<authentication>
<login-module code="com.palo.security.PaloLoginModule" flag="required"/>
</authentication>
</security-domain>
jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>PaloSecurityDomain</security-domain>
</jboss-web>
PaloLoginModule.java
package com.palo.security;
import java.security.acl.Group;
import java.util.Set;
import javax.inject.Inject;
import javax.naming.NamingException;
import javax.security.auth.login.LoginException;
import org.apache.log4j.Logger;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;
import com.palo.PaloRealmRole;
import com.palo.model.PaloRealmUser;
import com.palo.utils.CdiHelper;
import com.palo.utils.PasswordHandler;
public class PaloRealmLoginModule extends UsernamePasswordLoginModule {
private static Logger logger = Logger
.getLogger(PaloRealmLoginModule.class);
@Inject
private PaloRealmLogic realmLogic;
@Override
protected String getUsersPassword() throws LoginException {
if (null == realmLogic) {
try {
CdiHelper.programmaticInjection(PaloRealmLoginModule.class,
this);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
logger.debug("Getting password for user " + super.getUsername());
PaloRealmUser user = realmLogic.getUserByName(super.getUsername());
if (null == user) {
logger.error("User not found");
throw new LoginException("User " + super.getUsername()
+ " not found");
}
logger.debug("Found " + user.getPassword());
return user.getPassword();
}
@Override
protected Group[] getRoleSets() throws LoginException {
logger.debug("Getting roles for user " + super.getUsername());
if (null == realmLogic) {
try {
CdiHelper.programmaticInjection(PaloRealmLoginModule.class,
this);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
PaloRealmUser user = realmLogic.getUserByName(super.getUsername());
if (null == user) {
throw new LoginException("User " + super.getUsername()
+ " not found");
}
Set<PaloRealmRole> roles = user.getRoles();
Group[] groups = { new SimpleGroup("Roles") };
for (PaloRealmRole role : roles) {
logger.debug("Found role " + role.getRole());
SimplePrincipal prole = new SimplePrincipal(role.getRole());
groups[0].addMember(prole);
}
return groups;
}
@Override
protected boolean validatePassword(String inputPassword,
String expectedPassword) {
logger.debug("Validating password " + inputPassword + "|"
+ expectedPassword);
return PasswordHandler.getInstance().verifyPassword(inputPassword,
expectedPassword);
}
}
安全拦截器.java
package com.palo.web.rest;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.StringTokenizer;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.inject.Inject;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.apache.log4j.Logger;
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.Headers;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ServerResponse;
import com.palo.analytics.GoogleAnalyticsEvent;
import com.palo.logic.UserLogic;
import com.palo.web.utils.HttpUtils;
@Provider
@ServerInterceptor
public class SecurityInterceptor implements ContainerRequestFilter {
private static Logger logger = Logger.getLogger(SecurityInterceptor.class);
private static final String AUTHORIZATION_PROPERTY = "Authorization";
private static final ServerResponse ACCESS_DENIED = new ServerResponse(
"Access denied for this resource", 401, new Headers<Object>());
private static final ServerResponse ACCESS_DENIED_FOR_USER = new ServerResponse(
"User not authorized", 401, new Headers<Object>());
private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse(
"Nobody can access this resource", 403, new Headers<Object>());
@Inject
private UserLogic ul;
@Override
/**
* The request filter is called automatically called for each incoming request. It checks which method is being called by the client and, based on that method's annotations, restricts access, verifies the identity of the caller, checks the validity of the session token, etc.
*/
public void filter(ContainerRequestContext requestContext)
throws IOException {
logger.debug("------------- request filter ------------");
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) requestContext
.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
String methodName = method.getName();
String uri = requestContext.getUriInfo().getPath();
logger.debug("Accessing method " + methodName + " via URI " + uri);
for (String str : requestContext.getPropertyNames()) {
logger.debug(str);
}
// Get request headers
final MultivaluedMap<String, String> headers = requestContext
.getHeaders();
for (String key : headers.keySet()) {
for (String value : headers.get(key)) {
logger.debug(key + " - " + value);
}
}
// Access allowed for all
if (method.isAnnotationPresent(PermitAll.class)) {
return;
}
// Access denied for all
if (method.isAnnotationPresent(DenyAll.class)) {
requestContext.abortWith(ACCESS_FORBIDDEN);
return;
}
// Fetch authorization header
final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);
// If no authorization information present; block access
if (null == authorization || authorization.isEmpty()) {
requestContext.abortWith(ACCESS_DENIED);
return;
}
final String username = HttpUtils.getUsernameFromAuthorizationHeader(
authorization, HttpUtils.AUTHENTICATION_SCHEME_BASIC);
final String password = HttpUtils.getPasswordFromAuthenticationHeader(
authorization, HttpUtils.AUTHENTICATION_SCHEME_BASIC);
if (null == username || null == password || username.isEmpty()
|| password.isEmpty()) {
requestContext.abortWith(ACCESS_DENIED_FOR_USER);
return;
}
boolean authenticated = ul.authenticate(username, password);
if (false == authenticated) {
requestContext.abortWith(ACCESS_DENIED);
return;
}
return;
}
}
我正在使用 RESTClient for Firefox 将 REST 请求发送到 JAX-RS 方法。因为我记录了所有的 header ,所以我可以清楚地看到过滤器的内容,并且值在调用之间不会改变,即使我在 RESTClient 中更改它也是如此。更重要的是,即使我不在 RESTClient 中使用 Authorization header ,该值仍然存在。
我的问题是为什么授权 header 被阻止并且没有转发到我的过滤器?如果删除 web.xml 文件,我会在 ContainerRequestFilter 中获得正确的 Authorization header 。有没有办法将应用程序的/rest 部分移动到不受 web.xml 中的登录配置影响的区域?
非常感谢任何帮助!
最佳答案
据我了解,如果您指定了登录配置,那么它将用于所有在 web-resource-collection 中指定的资源。/rest/和/protected/在你的情况下。
第一种方法您可以做的一件事是修改您的登录模块,以便它为那些提供了有效凭据的用户分配 admin
角色,并为那些提供了有效凭据的用户分配 anonymous
角色未提供有效凭据。然后你可以像这样修改你的 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>anonymous</role-name>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>protected</web-resource-name>
<url-pattern>/protected/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
第二种方法不是修改您的登录模块,而是向您的安全域添加一个登录模块,这将为每个人分配 anonymous
角色
第三种方法使用自定义身份验证机制 http://undertow.io/documentation/core/security.htmlBASIC 身份验证机制期望用户以 http header 格式发送凭据授权:基本:base64encodedCredentials
当使用自定义身份验证机制时,您可以访问请求路径,并且您可以让自定义身份验证机制跳过对登录模块的调用,以防对您不想保护的路径发出请求.但我认为这不是一个好方法,因为这些决定应该由登录模块+web.xml 做出。
第四种方法(不确定是否有效,但希望它有效安全约束中未指定的资源不会被登录模块检查。因此,要使/rest/资源不 protected ,请从您的 web.xml 中删除这些行:
<security-constraint>
<web-resource-collection>
<web-resource-name>rest</web-resource-name>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
</security-constraint>
关于java - Wildfly web.xml 安全约束使用 ContainerRequestFilter 阻止 JAX-RS 方法的基本身份验证 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25354038/
我刚开始学习JSP技术,遇到了瓶颈。 如何从 JSP 声明 block ? 这不起作用: ... 服务器说没有“out”。 U: 我确实知道如何使用返回字符串的方法重写代码,但是有没有办法在 ?
在一个字段中,我想设置一个具有自定义过滤器的自定义分析器-着眼于词干-因此,“闪存卡”和“闪存卡”的词根相同,因此返回的结果相同 当我运行以下查询时,我的命中率很高,但是“闪存卡”和“闪存卡”各自返回
快速提问。 我有一个通过 PInvoke 使用 native DLL 的应用程序,这个 DLL 可能会调用 PostQuitMessage()。 如何避免? (因为我的应用程序不应该关闭) 我试过 A
一些给定的 HTML 文章,例如: Content 与一些基本的 Jquery 结合使用,例如: $(".some_
我正在构建一个灯箱相册。当第一个图像加载时,CSS 转换起作用。当加载后的每个图像都没有。任何想法为什么?加载第一张之后的照片,但没有过渡。 Image.prototype.load = functi
这个问题在这里已经有了答案: Disable recent tasks button on Android 5.0 (2 个答案) 关闭 2 年前。 我知道这个问题之前在这里被问过 Android
我是 Objective-C 的新手,我只是想弄清楚我是否可以使用 block 或选择器作为 UIAlertView 的 UIAlertViewDelegate 参数 - 哪个更合适? 我已经尝试了以
我是 Linux (UNIX) 套接字下套接字编程的新手。我在 Internet 上找到了以下代码,用于为每个连接生成一个线程的 tcp 服务器。但是它不起作用。accept() 函数立即返回,不等待
recv()库函数手册页提到: It returns the number of bytes received. It normally returns any data available, up
我有一个用于其他项目的共享 ts 库。在这个库中有被同一个库的其他资源使用的资源。该库的结构分为 components/*、interfaces/*、services/* 等目录。在每个目录的根目录中
我想在同一行中一个接一个地显示我的 ListView ,但 ListView 显示每个新行中的每个项目。我怎样才能防止换行显示。以便它显示为段落 ListView.builder( shr
我有一个包含数千行的表格。 import React from "react" import { useSelector } from "react-redux"; import { useEffec
假设我通常希望收到关于代码中不完整模式的警告,但有时我知道某个函数的模式不完整,我知道这很好。 是still true GHC 的警告粒度是每个模块的,并且没有办法更改有关特定功能或定义的警告? 最佳
我的网络应用程序发送浏览器通知,我知道如何检查通知的浏览器权限,以及如果未授予权限,如何请求权限。 但是,即使用户授予我的站点发送通知的权限,她可能仍然无法收到通知,因为它们 might be dis
我有 Xcode 3.2.1,并且喜欢使用它,但是当我编辑文本中带有超链接的文件时(例如,带有引用的注释:# see http://example.com)Xcode 将文本变成可点击的超链接。尝试编
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许在 Stack Overflow 上提出有关通用计算硬件和软件的问题。您可以编辑问题,使其成为
我有一个在 MY_Controller 中运行的 acl。如果权限被拒绝,那么此刻,我只是执行 redirect('denied') - 这是一个非常基本的 Controller ,它加载一个非常基本
我一直很好奇尝试从 Chrome 切换到 Firefox Quantum,但是对于 Web 开发遇到了一个我无法轻松解决的主要障碍——它正在缓存我的本地主机文件,因此当我尝试在本地主机加载各种 emb
这真的让我很兴奋!在任何时候,我都会参与多个项目。当我退出Xcode时,下次打开Xcode时,我前一天的所有项目都会自动一一打开。 经常我最终编辑错误的文件,AHHHHHHHHHHH!我可以阻止这种行
我的Wiki上有500个左右的Spambot和大约5个实际注册用户。我已经使用nuke删除了他们的页面,但是他们一直在重新发布。我已经使用reCaptcha控制了spambot的注册。现在,我只需要一
我是一名优秀的程序员,十分优秀!