On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru








 

Аутентификация в сервлетах

Владислав Каменский

Сервлет, т.к. он является веб приложением, располагаемым на серверной стороне может быть доступен множеству пользователей. Так же сервлеты могут работать с ресурсами этого сервера, и понятное дело, что рано или поздно возникает задача, ограничить, этот доступ. Грубо говоря, если вам однажды захочется, чтобы ваш сервлет прежде, чем выполнять ряд определенных функций, произвел аутентификацию, то можете смело читать эту статью.
Речь пойдет о Basic Authentication, механизм которой определен в спецификации HTTP 1.1. Данный механизм базируется, на связке имя - пароль. Веб сервер вопрошает веб клиент, что бы тот аутентифицировал пользователя. Веб клиент получает у пользователя, его имя и пароль, и пересылает их серверу. Basic Authentication не является секретным протоколом, так как, имя и пароль передаются серверу, посредством простого base64 кодирования. Да и к тому же сервер, на который высылается данная информация, не аутенифицируется. Т.е. если кто то, задастся целью, узнать пароли, к вашему сервлету, то он это сможет сделать(конечно, это простому смертному не доступно, но все же вероятность велика.) Таким образом, если вы пишите, приложения например для e-коммерции то данного механизма аутентификации будет недостаточно. Я бы посоветовал дополнительно использовать HTTPS соединение(эта тема в статье затронута не будет). Ну а для простенького приложения, этот механизм вполне сгодится.
Итак, переходя от слов к делу. Ставим две задачи.
1.Аутенитфикация сервлета, вызываемого из html формы.
2.Аутенитфикация сервлета, вызываемого из апплета.
Начнем с первой задачи....
располагаем в теле метода doGet() следующий код.

первая строчка вызывает функцию, которая проверяет в заголовке запроса, имя и пароль пользователя. Так как мы вызываем сервлет в первый раз, то понятное дело, что эта функция ничего не найдет, и секретную информацию мы не увидим. Вместо этого мы получим следующие строки

	res.setStatus(res.SC_UNAUTHORIZED);
// это, говорит о том, что пользователь не авторизован
res.setHeader("WWW-Authenticate","Basic realm=\"testarea\"");
// это выкинет на стороне пользователя, стандартное окно
// для ввода имени пользователя и пароля
После ввода пользователя, браузер передаст информацию введенную пользователем в следующем виде - "Basic + имя:пароль"(эта строчка будет закодирована посредством base64 кодирования). Сервлет вызовется снова, теперь посмотрим более детально на функцию

//Примечание: автор данной функции не я, а  Игорь Мазница.
public boolean getAuthorizedParameters(HttpServletRequest req) {
	// этот дружище поможет нам раскодировать связку имя+пароль
	sun.misc.BASE64Decoder bs64dec = new sun.misc.BASE64Decoder();
	boolean result = true;
	String NamePassword = null;
	// получим заголовок
	String auth_string = req.getHeader("Authorization");
	if (auth_string==null) return false;
	// получим схему, с помощью которой передан пароль              
	StringTokenizer st = new StringTokenizer(auth_string," ");
	String scheme = null;
	try{
		scheme = st.nextToken();  
	}catch (NoSuchElementException e){
		scheme = "";  
		result = false;
	}
	try{
		if (scheme.equalsIgnoreCase("BASIC")){  
			NamePassword = st.nextToken();  
			// раскодируем имя+пароль
  	    	NamePassword = new String(bs64dec.decodeBuffer(NamePassword)); 
		}else NamePassword = st.nextToken();
	}catch (NoSuchElementException e){
		NamePassword  = "";  
		result = false;
	}catch (IOException e){
		NamePassword  = ""; 
		result = false;
	}               
	st = null;
	st = new StringTokenizer(NamePassword,":");
	try{
		// получим имя пользователя 
		authUserName = st.nextToken();  
	}catch (NoSuchElementException e){
		authUserName = null;  
		result = false;
	}
	try{
		// получим пароль
		this.authUserPsswd = st.nextToken();   
	}catch (NoSuchElementException e){
		this.authUserPsswd = null; 
		result = false;
	}
	if(authUserName.equals("ИМЯ") && authUserPsswd.equals("ПАРОЛЬ"))
		result = true;
	else
		result = false;

   return result;
}
Вы наверное уловили, что при любой ошибке функция возвращает false, и следовательно, пользователь получит снова статус неавторизованного. В случае удачи пользователь увидит секретную информацию. Понятно, что данную функцию можно значительно сократить, и она является довольно избыточной, но мы оставим это на откуп читателям :)
Замечу, что если пользователь нажмет кнопку Cancel на форме ввода, то его взору предстанет страничка, которую формируют строки, следущие за

res.setStatus(res.SC_UNAUTHORIZED);
res.setHeader("WWW-Authenticate","Basic realm=\"realm\"");  
Если их не написать, то пользователь увидит пустую страничку. Логичным будет, если в этом месте выдать форму, содержащую прощальные слова, говорящие о том что мол, сам пользователь отменил аутентификацию. И дать ссылочку на этот же сервлет, если он захочет повторить попытку аутентификации снова.
Переходим к второй задаче ...
Например, соединяемся мы с сервлетом из апплета, причем понятное дело, что особо сервлет мы переписывать не будем. Нашей задачей будет соединиться с сервлетом, передать ему имя+пароль, по схеме Basic Authentication, и соответственно получить входной поток с секретной информацией :)))


URL url = new URL(host);	
HttpMessage msg = new HttpMessage(url);
msg.setAuthorization(String name, String password)
InputStream in = msg.sendPostMessage(null); 
BufferedReader data = new BufferedReader(new InputStreamReader(in));
nextMessage = "";
for (String line = data.readLine(); line != null; line = data.readLine()) {
nextMessage += line + "\n";	// это как вы понимаете и будет секретной информацией
Здесь я не буду детально обсуждать, технику соединения апплета и сервлета, это вы сможете найти в статье Доступ к БД из сервлета. Взаимосвязь апплет-сервлет. Решение проблемы русификации.
Остановимся лишь на классе HttpMessage и заглянем внутрь его методов. Этим методом, мы устанавливаем имя + пароль, для нашего соединения. Если пользоваться классом com.oreilly.servelt.HttpMessage, то в общем то этого вполне достаточно, для удачной аутентификации, вызываем метод sendPostMessage(null), и все получится.

public void setAuthorization(String name, String password){
	String authorization = Base64Encoder.encode(name + ":" + password);
	setHeader("Authorization", "Basic " + authorization);
}  
Но, для интереса, все же поглядим, как устроеy класс HttpMessage. Итак, метод setAuthorization вызывал следующий метод, после того как закодировал строчку "имя:пароль" с помощью кодировщика:

public void setHeader(String name, String value) {
	if (headers == null) {
	  headers = new Hashtable();
	}
	headers.put(name, value);
  }  
данный метод, как мы видим всего лишь заполняет Hashtable. Остается метод sendHeaders, который, будет вызван из метода sendPostMessage:

private void sendHeaders(URLConnection con) {
	if (headers != null) {
	// получим ключи для хэш таблицы
	  Enumeration enum = headers.keys();
	  while (enum.hasMoreElements()) {
		String name = (String) enum.nextElement();
		String value = (String) headers.get(name);
		// установим параметры connection
		//	Authorization -  "Basic имя:пароль"
		con.setRequestProperty(name, value);
	  }
	}
  }  
Вот вроде бы и все, все что нужно сделать это вызвать метод setRequestProperty(first,second) c двумя параметрами, первый = "Authorization", второй - "Basic имя:пароль", причем строчка "имя:пароль" закодирована простым base64 кодированием. Таким образом, в случае соединения апплет-сервлет, мы выполнили, работу, которую в случае 1 за нас делал Веб Клиент. Следует понимать, что в случае 1, после удачной авторизации, при следующих вызовах сервлета, браузер будет сам подставлять и имя и пароль. А вот в случае с апплетом, нам придется перед каждым вызовом сервлета устанавливать имя+пароль.То есть, лишний раз дергать метод: setAuthorization(String name, String password) класса com.oreilly.servelt.HttpMessage
Итак, мы рассмотрели механизм аутентификации в сервлетах. Для удачной работы вам понадобятся следующие классы:

com.oreilly.servelt.HttpMessage.java
com.oreilly.servelt.Base64Encoder.class
sun.misc.BASE64Decoder.class

Что касается, последних двух классов, то это кодировщик и раскодировщик, вместо них, можно использовать какие-либо другие, которые вы найдете, либо напишите сами:). Основная их задача - закодировать/раскодировать строчку, с помощью base64 кодирования.


Языки программирования: разное