Spring Security 4.1 default logout-url (/logout) doesn't seem to work

1.1k views Asked by At

I am experimenting with spring security and I encountered a problem that i can't solve for almost a day now.. My login form for default /login URL is working fine, csrf is enabled. onAuthenticationSuccess method in MyAuthenticationSuccessHandler is invoked and it is redirecting to desired page after successful login. My problem concerns /logout URL. I searched many pages for solution but couldn't find answer that fits. When i'm trying to logout via /logout URL in header.jsp HTTP Status 405 ? Method Not Allowed page is shown and the message is Request method 'POST' not supported. I'm sending logout form by POST

<form id="logoutForm" action="${pageContext.request.contextPath}/logout" method="POST" > <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" /> </form>

onLogoutSuccess method in MyLogoutSuccessHandler is invoked and user is logged out but redirection stops on /logout URL with that 405 error page. It can't reach my /loginController/showLogout redirection specified in MyLogoutSuccessHandler. I can't figure out what is messed up

This is my code:

security-context.xml

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

<jee:jndi-lookup jndi-name="jdbc/spring" id="dataSource"
    expected-type="javax.sql.DataSource">
</jee:jndi-lookup>

<bean id="activeUserStore" class="com.springtutorial.authentication.ActiveUserStore" />

<bean id="myAuthenticationSuccessHandler"
    class="com.springtutorial.authentication.MyAuthenticationSuccessHandler">
    <property name="activeUserStore" ref="activeUserStore"></property>
</bean>

<bean id="myLogoutSuccessHandler"
    class="com.springtutorial.authentication.MyLogoutSuccessHandler" />

<security:authentication-manager>
    <security:authentication-provider >
        <security:jdbc-user-service
            data-source-ref="dataSource"
            users-by-username-query="select username, password, enabled from simple_users where binary username = ?"
            authorities-by-username-query="select username, role from authorities where binary username = ?" />
        <security:password-encoder ref="passwordEncoder"></security:password-encoder>
    </security:authentication-provider>
</security:authentication-manager>
<security:http use-expressions="true">
    <security:intercept-url pattern="/loggedUsers"
        access="hasRole('ROLE_ADMIN')" />
    <security:intercept-url pattern="/adminPage"
        access="hasRole('ROLE_ADMIN')" /> 
    <security:intercept-url pattern="/offerController/processCreateOfferForm"
        access="isAuthenticated()" />
    <security:intercept-url pattern="/offerController/showCreateOfferForm"
        access="isAuthenticated()" />
    <security:intercept-url pattern="/offerController/offerCreated"
        access="isAuthenticated()" />
    <security:intercept-url pattern="/" access="permitAll" />
    <security:intercept-url pattern="/showAccessDenied"
        access="permitAll" />
    <security:intercept-url pattern="/loginController/showRegisterUserForm"
        access="permitAll" />
    <security:intercept-url pattern="/loginController/processRegisterUserForm"
        access="permitAll" />
    <security:intercept-url pattern="/loginController/userRegistered"
        access="permitAll" />
    <security:intercept-url pattern="/loginController/showLoginForm"
        access="permitAll" />
    <security:intercept-url pattern="/loginController/showLogout"
        access="permitAll" />
    <security:intercept-url pattern="/resources/**"
        access="permitAll" />
    <security:intercept-url pattern="/offerController/showOffers"
        access="permitAll" />
    <security:intercept-url pattern="/**" access="denyAll" />
    <security:form-login login-page="/loginController/showLoginForm"
        authentication-failure-url="/loginController/showLoginForm?error=true"
        authentication-success-handler-ref="myAuthenticationSuccessHandler" />
    <security:logout invalidate-session="true"
        success-handler-ref="myLogoutSuccessHandler" />
    <security:access-denied-handler error-page="/showAccessDenied" />
    <security:remember-me key="offersAppKey" />
    <security:csrf />
</security:http>

<bean id="passwordEncoder"
    class="org.springframework.security.crypto.password.StandardPasswordEncoder">
</bean>

I am using apache tiles 3 for my view and I'm redirecting to default /login URL in loginForm.jsp tile: loginForm.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<script type="text/javascript">
    $(document).ready(function() {
        document.username.focus()
    });
</script>
    <h3>My Login</h3>

    <c:if test="${param.error}">

    <p class="errors">Login failed. Username or password are wrong.</p>

    </c:if>

    <form  action='${pageContext.request.contextPath}/login' method='POST'>

        <table>
            <tr>
                <td>User:</td>
                <td><input type='text' id='username' name='username' ></td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type='password' name='password' /></td>
            </tr>
            <tr>
                <td>Remember me:</td>
                <td><input type='checkbox' name='remember-me' checked="checked" /></td>
            </tr>
            <tr>
                <td colspan='2'><input name="submit" type="submit"
                    value="Login" /></td>
            </tr>
            <tr>
                <td><input name="_csrf" type="hidden"
                    value="${_csrf.token}" /></td>
            </tr>
        </table>
    </form>
    <p><a href="<c:url value="showRegisterUserForm"/>">Create new User</a></p>

and redirecting to default /logout URL in header.jsp tile:

header.jsp

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

<a class="title"
        href="${pageContext.request.contextPath}/">Offers</a>   

<sec:authorize access="!isAuthenticated()">
    <a class="login"
        href="${pageContext.request.contextPath}/loginController/showLoginForm">Login</a>
</sec:authorize>

<sec:authorize access="isAuthenticated()">
    <!-- <a class="login" href="${pageContext.request.contextPath}/logout">Logout</a> -->
    <a class="login" id="logoutLink" href="#">Logout</a>
    <form id="logoutForm" action="${pageContext.request.contextPath}/logout" method="POST" >
          <input type="hidden" name="${_csrf.parameterName}"
            value="${_csrf.token}" />
    </form> 
</sec:authorize>

<script type="text/javascript">
    $(document).ready(function() {
        $('#logoutLink').click(function() {
            $('#logoutForm').submit();
        });
    });
</script>

And those are my implementations of AuthenticationSuccessHandler and LogoutSuccessHandler:

MyAuthenticationSuccessHandler

public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private ActiveUserStore activeUserStore;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response);
        HttpSession session = request.getSession(false);
        if (session != null) {
            LoggedUser user = new LoggedUser(authentication.getName(), activeUserStore);
            session.setAttribute("user", user);
        }

        if (savedRequest == null) {
            getRedirectStrategy().sendRedirect(request, response, "/");
        } else {
            String targetUrl = savedRequest.getRedirectUrl();
            getRedirectStrategy().sendRedirect(request, response, targetUrl);
        }
    }

    public ActiveUserStore getActiveUserStore() {
        return activeUserStore;
    }

    public void setActiveUserStore(ActiveUserStore activeUserStore) {
        this.activeUserStore = activeUserStore;
    }

}

MyLogoutSuccessHandler

public class MyLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {

@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
        throws IOException, ServletException {
    System.out.println("co jest");
    HttpSession session = request.getSession();
    if (session != null) {
        session.removeAttribute("user");
    }

    String URL = request.getContextPath() + "/loginController/showLogout";
    getRedirectStrategy().sendRedirect(request, response, URL);
}

}

I implemented those because i want to track logged in users :)

0

There are 0 answers