Вернуться в список

Logout в Spring Security

В этой статье будет рассказано о возможности выхода пользователя из системы (logout) и связанные с этим моменты в Spring Security версии 3.x.

Самый простой способ выйти для авторизированного пользователя - это использовать ссылку с действием /j_spring_security_logout. Это стандартное действие Spring Security, благодаря которому все действия по выходу пользователя из системы берет на себя Spring Security. Пример кода:

<%@ taglib uri ="http://java.sun.com/jsp/jstl/core" prefix ="c"%>
   <%@ taglib prefix ="sec"
    uri ="http://www.springframework.org/security/tags"%>
      <%@ page session ="false"%>
         <html>
            <body>

                <a href ="<c:url value="/j_spring_security_logout"/>">Logout</a>

            </body>
         </html>

Но давайте глубже разберемся в вопросе Logout. Как основа в этой статье будет использоваться пример из первой части статьи "Введение в Spring Security. Hello World!". Создайте проект, следуя инструкциям из статьи, или скачайте файл проекта для его использования в этой статье.

Первое, что сделаем, - изменим код таким образом, что ссылка Logout будет отображаться только авторизированным пользователям. Самый простой способ сделать это - использовать теги Spring Security. jsp-страница будет выглядеть следующим образом:

<%@ taglib uri ="http://java.sun.com/jsp/jstl/core" prefix ="c"%>
   <%@ taglib prefix ="sec"
    uri ="http://www.springframework.org/security/tags"%>
      <%@ page session ="false"%>
         <html>
            <head>
               <title>Home</title>
            </head>
            <body>
                
                <sec:authorize access ="isAuthenticated()">
                    <a href ="<c:url value="/j_spring_security_logout"/>">Logout</a>
                </sec:authorize>

            </body>
         </html>

Для того чтобы использовать теги Spring Security, необходимо добавить зависимость spring-security-taglibs в файл pom.xml:

<dependency>
    <groupid>org.springframework.security</groupid>
    <artifactid>spring-security-taglibs</artifactid>
    <version>${org.springframework-version}</version>
</dependency>

Весь файл pom.xml выглядит так:

<?xml version ="1.0" encoding ="UTF-8"?>
<project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemalocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupid>com.seostella</groupid>
    <artifactid>springsecuritylogout</artifactid>
    <name>spring-security-logout</name>
    <packaging>war</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>
    <properties>
        <java-version>1.6</java-version>
        <org.springframework-version>3.1.0.RELEASE</org.springframework-version>
        <org.aspectj-version>1.6.9</org.aspectj-version>
        <org.slf4j-version>1.5.10</org.slf4j-version>
    </properties>
    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-context</artifactid>
            <version>${org.springframework-version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupid>commons-logging</groupid>
                    <artifactid>commons-logging</artifactid>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-webmvc</artifactid>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- Spring Security -->
        <dependency>
            <groupid>org.springframework.security</groupid>
            <artifactid>spring-security-core</artifactid>
            <version>${org.springframework-version}</version>
        </dependency>

        <dependency>
            <groupid>org.springframework.security</groupid>
            <artifactid>spring-security-web</artifactid>
            <version>${org.springframework-version}</version>
        </dependency>

        <dependency>
            <groupid>org.springframework.security</groupid>
            <artifactid>spring-security-config</artifactid>
            <version>${org.springframework-version}</version>
        </dependency>

        <dependency>
            <groupid>org.springframework.security</groupid>
            <artifactid>spring-security-taglibs</artifactid>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- AspectJ -->
        <dependency>
            <groupid>org.aspectj</groupid>
            <artifactid>aspectjrt</artifactid>
            <version>${org.aspectj-version}</version>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupid>org.slf4j</groupid>
            <artifactid>slf4j-api</artifactid>
            <version>${org.slf4j-version}</version>
        </dependency>
        <dependency>
            <groupid>org.slf4j</groupid>
            <artifactid>jcl-over-slf4j</artifactid>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupid>org.slf4j</groupid>
            <artifactid>slf4j-log4j12</artifactid>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupid>log4j</groupid>
            <artifactid>log4j</artifactid>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupid>javax.mail</groupid>
                    <artifactid>mail</artifactid>
                </exclusion>
                <exclusion>
                    <groupid>javax.jms</groupid>
                    <artifactid>jms</artifactid>
                </exclusion>
                <exclusion>
                    <groupid>com.sun.jdmk</groupid>
                    <artifactid>jmxtools</artifactid>
                </exclusion>
                <exclusion>
                    <groupid>com.sun.jmx</groupid>
                    <artifactid>jmxri</artifactid>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

        <!-- @Inject -->
        <dependency>
            <groupid>javax.inject</groupid>
            <artifactid>javax.inject</artifactid>
            <version>1</version>
        </dependency>

        <!-- Servlet -->
        <dependency>
            <groupid>javax.servlet</groupid>
            <artifactid>servlet-api</artifactid>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupid>javax.servlet.jsp</groupid>
            <artifactid>jsp-api</artifactid>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupid>javax.servlet</groupid>
            <artifactid>jstl</artifactid>
            <version>1.2</version>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupid>junit</groupid>
            <artifactid>junit</artifactid>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactid>maven-eclipse-plugin</artifactid>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupid>org.apache.maven.plugins</groupid>
                <artifactid>maven-compiler-plugin</artifactid>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupid>org.codehaus.mojo</groupid>
                <artifactid>exec-maven-plugin</artifactid>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Теперь в jsp-файлах есть возможность использовать теги Spring Security. Нам понадобится только один из них - authorize (остальным тегам будет посвящена отдельная статья). Добавьте в jsp-файле, в котором Вы хотите использовать ссылку Logout, следующую библиотеку:

<%@ taglib prefix ="sec"
 uri ="http://www.springframework.org/security/tags"%>

А ссылка будет выглядеть так:

<sec:authorize access ="isAuthenticated()">
    <a href ="<c:url value="/j_spring_security_logout"/>">Logout</a>
</sec:authorize>

Содержимое тега sec:authorize будет отображено только если пользователь вошел в систему, что описывается выражением isAuthenticated().

Тем не менее, чтобы использовать выражение isAuthenticated() в jsp-файле необходимо изменить конфигурацию безопасности (файл application-security.xml) следующим образом. Блок http:

<http auto-config ="true">
    <intercept-url pattern ="/secure/**" access ="ROLE_USER" />
    <intercept-url pattern ="/admin/**" access ="ROLE_ADMIN" />
</http>

заменить на следующий код

<http auto-config ="true" use-expressions ="true">
    <intercept-url pattern ="/secure/**" access ="hasRole('ROLE_USER')" />
    <intercept-url pattern ="/admin/**" access ="hasRole('ROLE_ADMIN')" />
</http>

Всё дело в атрибуте use-expressions тега http. Этим тегом в Spring Security обозначается, что будут использоваться выражения. Их (выражений) существует множество и следующая часть статей о Spring Security будет посвящена им.

Также есть возможность изменить адрес /j_spring_security_logout для выхода из системы. Всё, что нужно, - добавить в тег http тег logout с атрибутом logout-url:

<http auto-config ="true" use-expressions ="true">
    <intercept-url pattern ="/secure/**" access ="hasRole('ROLE_USER')" />
    <intercept-url pattern ="/admin/**" access ="hasRole('ROLE_ADMIN')" />
    <logout logout-url ="/logout" />
</http>

Теперь jsp-страницу можно изменить адрес для Logout:

<%@ taglib prefix ="sec"
 uri ="http://www.springframework.org/security/tags"%>

А ссылка будет выглядеть так:

<sec:authorize access ="isAuthenticated()">
    <a href ="<c:url value="/logout"/>">Logout</a>
</sec:authorize>

У тега logout есть еще несколько полезных атрибутов:

Атрибут delete-cookies - это список куки (cookies), разделенный запятыми, которые будут удалены, когда пользователь выйдет из системы.

Атрибут invalidate-session - если Вы хотите аннулировать сессию при выходе пользователя из системы, установите этот атрибут в true, иначе - false. По умолчанию - true.

Атрибут logout-success-url - адрес, на который будет перенаправлен пользователь после того, как он вышел из системы. По умолчанию - корень "/".

Атрибут logout-url - адрес, используя который пользователь выйдет из системы. По умолчанию - "/j_spring_security_logout".

Атрибут success-handler-ref - сервис, унаследованный от LogoutSuccessHandler, который контролирует навигацию пользователя после того, как последний вышел из системы.

Рассмотрим пример с последним атрибутом success-handler-ref. Изменим тег logout следующим образом:

<http auto-config ="true" use-expressions ="true">
    <intercept-url pattern ="/secure/**" access ="hasRole('ROLE_USER')" />
    <intercept-url pattern ="/admin/**" access ="hasRole('ROLE_ADMIN')" />
    <logout logout-url ="/logout" success-handler-ref ="customLogoutSuccessHandler"/>
</http>

Код сервиса CustomLogoutSuccessHandler представлен ниже:

package com.seostella.springsecuritylogout.service;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
import org.springframework.stereotype.Service;

@Service
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {

    @Override
   public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, 
     Authentication authentication) throws IOException, ServletException {
       
      Object principal = authentication.getPrincipal();
      if (principal instanceof User) {
         User user = (User) principal;
         if ( user.getUsername().equals("user") ) {
            response.sendRedirect( request.getContextPath() + "/logout/user" );
         }
      }
      response.sendRedirect(  request.getContextPath() + "/" );
       
   }
}

Класс этого сервиса должен быть унаследован от SimpleUrlLogoutSuccessHandler. Если Вы хотите изменить логику выхода пользователя из системы, необходимо переопределить метод onLogoutSuccess(). Также в этом методе Вы можете произвести необходимые Вам действия для обработки выхода пользователя из системы.

В нашем примере если имя пользователя user, то он перенаправляется на адрес "/logout/user", все остальные пользователи после выхода попадают на домашнюю страницу.

Чтобы этот код работал необходимо сделать адрес "/logout/user" доступным для незарегестрированных пользователей. То есть, добавить в application-security.xml следующую строку:


<http pattern ="/logout/user/" security ="none" />

Также потребуется немного изменить структуру конфигурации проекта:

web.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<web-app version ="2.5" 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_2_5.xsd">

    <!-- The definition of the Root Spring Container shared by all Servlets 
    and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/root-context.xml
            /WEB-INF/spring/application-security.xml
        </param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


</web-app>

root-context.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<beans:beans xmlns ="http://www.springframework.org/schema/mvc"
 xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:beans ="http://www.springframework.org/schema/beans"
 xmlns:context ="http://www.springframework.org/schema/context"
 xsi:schemalocation ="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
 <annotation-driven />

 <resources mapping ="/resources/**" location ="/resources/" />

 <beans:bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver">
     <beans:property name ="prefix" value ="/WEB-INF/views/" />
     <beans:property name ="suffix" value =".jsp" />
 </beans:bean>
 
 <context:component-scan base-package ="com.seostella.springsecuritylogout" />
 
</beans:beans>

application-security.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<beans:beans xmlns ="http://www.springframework.org/schema/security"
 xmlns:beans ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemalocation ="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security-3.1.xsd">

 <http pattern ="/css/**" security ="none" />
 <http pattern ="/logout/user/" security ="none" />
 
 <http auto-config ="true" use-expressions ="true">
     <intercept-url pattern ="/secure/**" access ="hasRole('ROLE_USER')" />
     <intercept-url pattern ="/admin/**" access ="hasRole('ROLE_ADMIN')" />
     <logout logout-url ="/logout" success-handler-ref ="customLogoutSuccessHandler"/>
 </http>
 

 <authentication-manager>
     <authentication-provider>
         <user-service>
             <user name ="admin" password ="adminpassword" authorities ="ROLE_USER, ROLE_ADMIN" />
             <user name ="user" password ="userpassword" authorities ="ROLE_USER" />
         </user-service>
     </authentication-provider>
 </authentication-manager>
</beans:beans>

servlet-context.xml

<?xml version ="1.0" encoding ="UTF-8"?>
<beans:beans xmlns ="http://www.springframework.org/schema/mvc"
 xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:beans ="http://www.springframework.org/schema/beans"
 xmlns:context ="http://www.springframework.org/schema/context"
 xsi:schemalocation ="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

</beans:beans>

На этом закончим рассмотрение logout.

Скачать рабочий проект Вы можете по следующей ссылке - Скачать spring-security-logout.zip.

21.08.2015

    Только зарегистрированные пользователи могут оставить комментарий.
    Вернуться в список
    2017 «Инфокристалл» Сборка от 18.04.2017 03:58
    Контакты | Задать вопрос

    Карта сайта