- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一个 Spring Data 项目,它使用 RepositoryRestResource 和 CrudRepository 来通过 REST 公开实体。我需要能够在通过 HTTP 访问时保护存储库,但在内部使用时(例如在服务层中)不保护它。
我已经启动并运行了 Spring Security,但是在 CrudRepository 方法上添加像 PreAuthorize 这样的注释,也会导致在我从我的服务层中调用这些方法时执行安全表达式。
如果有人能用这个为我指明正确的方向,我会很高兴。
编辑 1
我已经尝试从 UserRepository 中删除 Rest Export 和安全注释以供内部使用,然后将 UserRepository 子类化为 UserRepositoryRestExported,导出并保护它。但是,我看到运行之间安全注释的一些不一致实现,这让我想知道 Spring 是否有时导出 UserRepositoryRestExported,而其他时候导出 UserRepository ...?
编辑 2
这是编辑 1 中描述的代码
用户库.java
@Component("UserRepository")
public interface UserRepository extends CrudRepository<User, Long> {
// .. some extra methods
}
UserRepositoryRest.java
@Component("UserRepositoryRest")
@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepositoryRest extends UserRepository {
@PostAuthorize("authentication.name == returnObject.getName() || hasRole('ROLE_ADMIN')")
@Override
User findOne(Long id);
@PostFilter("authentication.name == filterObject.getName() || hasRole('ROLE_ADMIN')")
@Override
Iterable<User> findAll();
@PreAuthorize("principal.getCell() == #user.getName() || hasRole('ROLE_ADMIN')")
@Override
void delete(@P("user") User user);
User save(User entity);
long count();
boolean exists(Long primaryKey);
}
最佳答案
编辑:我不再推荐这个了——我最终只是滚动了我自己的 REST Controller ,因为它变得太 hacky 和不可预测了。否则see here for a possible alternative .
可以实现本文标题中的目标,但由于 Spring 官方不支持,所以有点复杂。
粗略地说,您必须创建两个存储库,一个供内部使用,另一个(安全)供外部使用。然后你必须修改 spring 以便它只导出一个供外部使用。
大部分代码来自下面链接的帖子;非常感谢 Will Faithful 提出了解决方案:
错误票:https://jira.spring.io/browse/DATAREST-923
修复存储库:https://github.com/wfaithfull/spring-data-rest-multiple-repositories-workaround
第一步
创建仅供内部使用的不安全、未导出的存储库:
@RepositoryRestResource(exported = false)
@Component("UserRepository")
public interface UserRepository extends CrudRepository<User, Long> { }
请注意没有安全注释(例如@PreAuthorized)并且@RepositoryRestResource 设置为exported=false。
第 2 步
创建仅通过 HTTP REST 使用的安全导出存储库:
@Component("UserRepositoryRest")
@Primary
@RepositoryRestResource(collectionResourceRel = "users", path = "users", exported = true)
public interface UserRepositoryRest extends UserRepository {
@PostAuthorize(" principal.getUsername() == returnObject.getUsername() || hasRole('ROLE_ADMIN') ")
@Override
User findOne(Long id);
}
注意这里我们使用了安全注释,并且我们使用 exported=true 显式导出存储库。
第 3 步
这是它变得有点复杂的地方。如果您停在这里,Spring 有时会加载并尝试导出您的 UserRepository 类,有时会加载并尝试导出您的 UserRepositoryRest 类。这可能会导致单元测试偶尔失败(大约 50% 的时间),以及其他奇怪的副作用,导致难以追踪。
我们将通过调整 Spring 选择导出存储库的方式来解决这个问题。创建一个包含以下内容的文件:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.support.RepositoryFactoryInformation;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.io.Serializable;
import java.util.*;
/**
* @author Will Faithfull
*
* Warning: Ugly hack territory.
*
* Firstly, I can't just swap out this implementation, because Repositories is referenced everywhere directly without an
* interface.
*
* Unfortunately, the offending code is in a private method, {@link #cacheRepositoryFactory(String)}, and modifies private
* fields in the Repositories class. This means we can either use reflection, or replicate the functionality of the class.
*
* In this instance, I've chosen to do the latter because it's simpler, and most of this code is a simple copy/paste from
* Repositories. The superclass is given an empty bean factory to satisfy it's constructor demands, and ensure that
* it will keep as little redundant state as possible.
*/
public class ExportAwareRepositories extends Repositories {
static final Repositories NONE = new ExportAwareRepositories();
private static final RepositoryFactoryInformation<Object, Serializable> EMPTY_REPOSITORY_FACTORY_INFO = EmptyRepositoryFactoryInformation.INSTANCE;
private static final String DOMAIN_TYPE_MUST_NOT_BE_NULL = "Domain type must not be null!";
private final BeanFactory beanFactory;
private final Map<Class<?>, String> repositoryBeanNames;
private final Map<Class<?>, RepositoryFactoryInformation<Object, Serializable>> repositoryFactoryInfos;
/**
* Constructor to create the {@link #NONE} instance.
*/
private ExportAwareRepositories() {
/* Mug off the superclass with an empty beanfactory to placate the Assert.notNull */
super(new DefaultListableBeanFactory());
this.beanFactory = null;
this.repositoryBeanNames = Collections.<Class<?>, String> emptyMap();
this.repositoryFactoryInfos = Collections.<Class<?>, RepositoryFactoryInformation<Object, Serializable>> emptyMap();
}
/**
* Creates a new {@link Repositories} instance by looking up the repository instances and meta information from the
* given {@link ListableBeanFactory}.
*
* @param factory must not be {@literal null}.
*/
public ExportAwareRepositories(ListableBeanFactory factory) {
/* Mug off the superclass with an empty beanfactory to placate the Assert.notNull */
super(new DefaultListableBeanFactory());
Assert.notNull(factory, "Factory must not be null!");
this.beanFactory = factory;
this.repositoryFactoryInfos = new HashMap<Class<?>, RepositoryFactoryInformation<Object, Serializable>>();
this.repositoryBeanNames = new HashMap<Class<?>, String>();
populateRepositoryFactoryInformation(factory);
}
private void populateRepositoryFactoryInformation(ListableBeanFactory factory) {
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, RepositoryFactoryInformation.class,
false, false)) {
cacheRepositoryFactory(name);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private synchronized void cacheRepositoryFactory(String name) {
RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.getBean(name,
RepositoryFactoryInformation.class);
Class<?> domainType = ClassUtils
.getUserClass(repositoryFactoryInformation.getRepositoryInformation().getDomainType());
RepositoryInformation information = repositoryFactoryInformation.getRepositoryInformation();
Set<Class<?>> alternativeDomainTypes = information.getAlternativeDomainTypes();
String beanName = BeanFactoryUtils.transformedBeanName(name);
Set<Class<?>> typesToRegister = new HashSet<Class<?>>(alternativeDomainTypes.size() + 1);
typesToRegister.add(domainType);
typesToRegister.addAll(alternativeDomainTypes);
for (Class<?> type : typesToRegister) {
// I still want to add repositories if they don't have an exported counterpart, so we eagerly add repositories
// but then check whether to supercede them. If you have more than one repository with exported=true, clearly
// the last one that arrives here will be the registered one. I don't know why anyone would do this though.
if(this.repositoryFactoryInfos.containsKey(type)) {
Class<?> repoInterface = information.getRepositoryInterface();
if(repoInterface.isAnnotationPresent(RepositoryRestResource.class)) {
boolean exported = repoInterface.getAnnotation(RepositoryRestResource.class).exported();
if(exported) { // Then this has priority.
this.repositoryFactoryInfos.put(type, repositoryFactoryInformation);
this.repositoryBeanNames.put(type, beanName);
}
}
} else {
this.repositoryFactoryInfos.put(type, repositoryFactoryInformation);
this.repositoryBeanNames.put(type, beanName);
}
}
}
/**
* Returns whether we have a repository instance registered to manage instances of the given domain class.
*
* @param domainClass must not be {@literal null}.
* @return
*/
@Override
public boolean hasRepositoryFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
return repositoryFactoryInfos.containsKey(domainClass);
}
/**
* Returns the repository managing the given domain class.
*
* @param domainClass must not be {@literal null}.
* @return
*/
@Override
public Object getRepositoryFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
String repositoryBeanName = repositoryBeanNames.get(domainClass);
return repositoryBeanName == null || beanFactory == null ? null : beanFactory.getBean(repositoryBeanName);
}
/**
* Returns the {@link RepositoryFactoryInformation} for the given domain class. The given <code>code</code> is
* converted to the actual user class if necessary, @see ClassUtils#getUserClass.
*
* @param domainClass must not be {@literal null}.
* @return the {@link RepositoryFactoryInformation} for the given domain class or {@literal null} if no repository
* registered for this domain class.
*/
private RepositoryFactoryInformation<Object, Serializable> getRepositoryFactoryInfoFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
Class<?> userType = ClassUtils.getUserClass(domainClass);
RepositoryFactoryInformation<Object, Serializable> repositoryInfo = repositoryFactoryInfos.get(userType);
if (repositoryInfo != null) {
return repositoryInfo;
}
if (!userType.equals(Object.class)) {
return getRepositoryFactoryInfoFor(userType.getSuperclass());
}
return EMPTY_REPOSITORY_FACTORY_INFO;
}
/**
* Returns the {@link EntityInformation} for the given domain class.
*
* @param domainClass must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
@Override
public <T, S extends Serializable> EntityInformation<T, S> getEntityInformationFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
return (EntityInformation<T, S>) getRepositoryFactoryInfoFor(domainClass).getEntityInformation();
}
/**
* Returns the {@link RepositoryInformation} for the given domain class.
*
* @param domainClass must not be {@literal null}.
* @return the {@link RepositoryInformation} for the given domain class or {@literal null} if no repository registered
* for this domain class.
*/
@Override
public RepositoryInformation getRepositoryInformationFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
RepositoryFactoryInformation<Object, Serializable> information = getRepositoryFactoryInfoFor(domainClass);
return information == EMPTY_REPOSITORY_FACTORY_INFO ? null : information.getRepositoryInformation();
}
/**
* Returns the {@link RepositoryInformation} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
* @return the {@link RepositoryInformation} for the given repository interface or {@literal null} there's no
* repository instance registered for the given interface.
* @since 1.12
*/
@Override
public RepositoryInformation getRepositoryInformation(Class<?> repositoryInterface) {
for (RepositoryFactoryInformation<Object, Serializable> factoryInformation : repositoryFactoryInfos.values()) {
RepositoryInformation information = factoryInformation.getRepositoryInformation();
if (information.getRepositoryInterface().equals(repositoryInterface)) {
return information;
}
}
return null;
}
/**
* Returns the {@link PersistentEntity} for the given domain class. Might return {@literal null} in case the module
* storing the given domain class does not support the mapping subsystem.
*
* @param domainClass must not be {@literal null}.
* @return the {@link PersistentEntity} for the given domain class or {@literal null} if no repository is registered
* for the domain class or the repository is not backed by a {@link MappingContext} implementation.
*/
@Override
public PersistentEntity<?, ?> getPersistentEntity(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
return getRepositoryFactoryInfoFor(domainClass).getPersistentEntity();
}
/**
* Returns the {@link QueryMethod}s contained in the repository managing the given domain class.
*
* @param domainClass must not be {@literal null}.
* @return
*/
@Override
public List<QueryMethod> getQueryMethodsFor(Class<?> domainClass) {
Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
return getRepositoryFactoryInfoFor(domainClass).getQueryMethods();
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Class<?>> iterator() {
return repositoryFactoryInfos.keySet().iterator();
}
/**
* Null-object to avoid nasty {@literal null} checks in cache lookups.
*
* @author Thomas Darimont
*/
private static enum EmptyRepositoryFactoryInformation implements RepositoryFactoryInformation<Object, Serializable> {
INSTANCE;
@Override
public EntityInformation<Object, Serializable> getEntityInformation() {
return null;
}
@Override
public RepositoryInformation getRepositoryInformation() {
return null;
}
@Override
public PersistentEntity<?, ?> getPersistentEntity() {
return null;
}
@Override
public List<QueryMethod> getQueryMethods() {
return Collections.<QueryMethod> emptyList();
}
}
}
第四步
创建另一个包含以下内容的文件:
import me.faithfull.hack.ExportAwareRepositories;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
/**
* @author Will Faithfull
*/
@Configuration
public class RepositoryRestConfiguration extends RepositoryRestMvcConfiguration {
@Autowired
ApplicationContext context;
/**
* We replace the stock repostiories with our modified subclass.
*/
@Override
public Repositories repositories() {
return new ExportAwareRepositories(context);
}
}
利润
应该这样做 - Spring 现在应该正确地只导出您的 UserRepositoryRest 类,同时忽略您的 UserRepository 类供您在内部使用而没有安全限制。
关于java - 通过 HTTP 保护 Spring Data RepositoryRestResource (CrudRepository),但不是在内部,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43265476/
据我了解,HTTP POST 请求的正文大小没有限制。因此,客户端可能会在一个 HTTP 请求中发送 千兆字节 的数据。现在我想知道 HTTP 服务器应该如何处理此类请求。 Tomcat 和 Jett
在了解Web Deploy我遇到了一些讨论 http://+:80 和 http://*:80 的 netsh.exe 命令。这些是什么意思? 最佳答案 引自URLPrefix Strings (Wi
假设我有一个负载均衡器,然后是 2 个 Web 服务器,然后是一个负载均衡器,然后是 4 个应用程序服务器。 HTTP 响应是否遵循与 HTTP 请求服务相同的路径? 最佳答案 按路径,我假设您是网络
我有一个带有 uri /api/books/122 的资源,如果在客户端为此资源发送 HTTP Delete 时该资源不存在,那么相应的响应代码是什么这个 Action ?是不是404 Not Fou
是否有特定的(或约定的)HTTP 响应消息(或除断开连接之外的其他操作)来阐明服务器不接受 pipelined HTTP requests ? 我正在寻找能让客户端停止流水线化它的请求并分别发送每个请
在了解Web Deploy我遇到了一些讨论 http://+:80 和 http://*:80 的 netsh.exe 命令。这些是什么意思? 最佳答案 引自URLPrefix Strings (Wi
我有一个带有 uri /api/books/122 的资源,如果在客户端为此资源发送 HTTP Delete 时该资源不存在,那么相应的响应代码是什么这个 Action ?是不是404 Not Fou
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭 8 年前。 Improve this qu
我使用 Mule 作为 REST API AMQP。我必须发送自定义请求方法:“PRINT”,但我收到: Status Code: 400 Bad Request The request could
我需要针对具有不同 HTTP 响应代码的 URL 测试我的脚本。我如何获取响应代码 300、303 或 307 等的示例/示例现有 URL? 谢谢! 最佳答案 您可以使用 httpbin为此目的。 例
我正在尝试编写一个程序来匹配 HTTP 请求及其相应的响应。似乎在大多数情况下一切都运行良好(当传输完全有序时,即使不是,通过使用 TCP 序列号)。 我发现的唯一问题是当我有流水线请求时。在那之后,
RESTful Web Services鼓励使用 HTTP 303将客户端重定向到资源的规范表示。它仅在 HTTP GET 的上下文中讨论主题。 这是否也适用于其他 HTTP 方法?如果客户端尝试对非
当使用chunked HTTP传输编码时,为什么服务器需要同时写出chunk的字节大小并且后续的chunk数据以CRLF结尾? 这不会使发送二进制数据“CRLF-unclean”和方法有点多余吗? 如
这个问题在这里已经有了答案: Is it acceptable for a server to send a HTTP response before the entire request has
如果我向同一台服务器发出多个 HTTP Get 请求并收到每个请求的 HTTP 200 OK 响应,我如何使用 Wireshark 判断哪个请求映射到哪个响应? 目前看起来像是发出了一个 http 请
func main() { http.HandleFunc("/", handler) } func handler(w http.ResponseWriter, r http.Request
我找不到有值(value)的 NodeJS with Typescript 教程,所以我在无指导下潜入水中,果然我有一个问题。 我不明白这两行之间的区别: import * as http from
问一个关于Are HTTP headers case-sensitive?的问题,如果 HTTP 方法区分大小写,大多数服务器如何处理“get”或“post”与“GET”或“POST”? 例如,看起来
我正在使用ASP.NET,在其中我通过动词GET接收查询,该应用程序专用于该URL。 该代码有效,但是如果用户发送的密码使http 200无效,请回答我,并在消息的正文中显示“Fail user or
Closed. This question needs details or clarity。它当前不接受答案。 想改善这个问题吗?添加详细信息,并通过editing this post阐明问题。 9
我是一名优秀的程序员,十分优秀!