#security #tomcat #servlets #jakarta-ee
#Безопасность #tomcat #сервлеты #джакарта-ee
Вопрос:
Я создаю сервис, который, среди прочего, имеет функцию «фотоальбомы», которые предоставляют фотографии пользователям. Пользователю должно быть «разрешено» просматривать фотографию из альбома. Таким образом, отправка прямой ссылки другому лицу не должна позволять просматривать фотографию.
Фотографии хранятся в папке вне контекста.
Что мне нужно сделать, так это выполнить некоторые проверки, когда пользователь запрашивает фотографию, а затем, если проверки в порядке, отправить файл. Я хочу избежать создания колеса и просто позволить tomcat обслуживать изображение, как это обычно делается для статических файлов. Можете ли вы дать несколько советов по этому поводу?
Комментарии:
1. Какая информация требуется для этих проверок и где эта информация хранится?
2. Только сеанс. Пользователь должен быть зарегистрирован и должен иметь доступ, описанный в его профиле, который является объектом сеанса.
Ответ №1:
Хорошо, ребята.
После упорной борьбы с этим вопросом, я думаю, я наконец узнал, что нужно сделать, чтобы решить его. Прежде всего, похоже, что вопрос фактически разлагается на две независимые задачи. Одним из них является обеспечение доступа к некоторым ресурсам, а вторым — загрузка ресурсов из папки вне контекста.
Первая задача тривиальна и может быть решена путем написания простого фильтра, привязанного к «/».
Вторая задача гораздо менее тривиальна, но, к счастью, также может быть решена. Tomcat использует реализацию javax.naming.directory.DirContext для загрузки всех ресурсов данного веб-приложения, включая файлы классов. Это также позволяет вам предоставить пользовательскую реализацию этого интерфейса и настроить его в context.xml файл. Реализацией по умолчанию является org.apache.naming.resources.FileDirContext. Подробности здесь: http://tomcat.apache.org/tomcat-6.0-doc/config/resources.html
Я создал свою собственную реализацию DirContext, просто расширив FileDirContext. К счастью, существовал единственный метод, который пришлось перезаписать, чтобы «подключить» обнаружение файлов. Метод называется file().
Я публикую здесь свой тестовый код. Он далек от совершенства и не учитывает второстепенные случаи, такие как переименование файлов, но я не думаю, что они необходимы при обычном запуске сервера.
Основная идея этого кода состоит в том, чтобы проверить, начинается ли путь с префикса «виртуальный каталог», и если это так — выполнить поиск файла в другом месте файловой системы (я знаю, что там есть какой-то дублирующий код, но я надеюсь, вы не поленитесь удалить его, если когда-нибудь захотите его использовать :-). setVirtualName и setVirtualBase вызываются автоматически для ввода параметров конфигурации.
/**
* TODO: add javadocs
*
* @author Juriy Bura
*/
public class VirtualFolderDirContext extends FileDirContext {
private String virtualName;
private String realName;
private File virtualBase;
private String absoluteVirtualBase;
public VirtualFolderDirContext() {
super();
}
public VirtualFolderDirContext(Hashtable env) {
super(env);
}
public void setVirtualName(String path) {
virtualName = path;
}
public void setVirtualBase(String base) {
this.realName = base;
virtualBase = new File(realName);
try {
virtualBase = virtualBase.getCanonicalFile();
} catch (IOException e) {
// Ignore
}
this.absoluteVirtualBase = virtualBase.getAbsolutePath();
}
protected File file(String name) {
File file = null;
boolean virtualFile = name.startsWith(virtualName "/");
if (virtualFile) {
file = new File(virtualBase, name.substring(virtualName.length()));
} else {
file = new File(base, name);
}
if (file.exists() amp;amp; file.canRead()) {
if (allowLinking)
return file;
// Check that this file belongs to our root path
String canPath = null;
try {
canPath = file.getCanonicalPath();
} catch (IOException e) {
}
if (canPath == null)
return null;
// Check to see if going outside of the web application root
if (!canPath.startsWith(absoluteBase) amp;amp; !canPath.startsWith(absoluteVirtualBase)) {
return null;
}
// Case sensitivity check
if (caseSensitive) {
String fileAbsPath = file.getAbsolutePath();
if (fileAbsPath.endsWith("."))
fileAbsPath = fileAbsPath "/";
String absPath = normalize(fileAbsPath);
if (canPath != null)
canPath = normalize(canPath);
if (virtualFile) {
if ((absoluteVirtualBase.length() < absPath.length())
amp;amp; (absoluteVirtualBase.length() < canPath.length())) {
absPath = absPath.substring(absoluteVirtualBase.length() 1);
if ((canPath == null) || (absPath == null))
return null;
if (absPath.equals(""))
absPath = "/";
canPath = canPath.substring(absoluteVirtualBase.length() 1);
if (canPath.equals(""))
canPath = "/";
if (!canPath.equals(absPath))
return null;
}
} else {
if ((absoluteBase.length() < absPath.length())
amp;amp; (absoluteBase.length() < canPath.length())) {
absPath = absPath.substring(absoluteBase.length() 1);
if ((canPath == null) || (absPath == null))
return null;
if (absPath.equals(""))
absPath = "/";
canPath = canPath.substring(absoluteBase.length() 1);
if (canPath.equals(""))
canPath = "/";
if (!canPath.equals(absPath))
return null;
}
}
}
} else {
return null;
}
return file;
}
}
После того, как у вас будет этот класс, вы должны создать его jar и поместить этот jar в папку Tomcat lib. По очевидным причинам он не может работать вместе с файлом war. В вашем context.xml вы должны добавить строки конфигурации, подобные этим:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="true" antiJARLocking="true">
<Resources
className="com.juriy.tomcat.virtualdir.VirtualFolderDirContext"
virtualName="/upload"
virtualBase="c:/temp/up">
</Resources>
...
...
Теперь каждый раз, когда пользователь запрашивает / загрузить /, он будет разрешен на c:temp. С помощью этого метода вы можете реализовать загрузку ресурсов практически из любого местоположения: http, общей папки, базы данных, даже системы контроля версий. Так что это довольно круто.
P.S. Я убил целый день, чтобы все это сработало вместе, так что не стесняйтесь отдать мне свой голос, если вам нравится ответ :-))
Приветствия
Юрий