According to Spring Security 4.0.0 document:
4.2.4 Logout Handling
The logout element adds support for logging out by navigating to a
particular URL. The default logout URL is /logout, but you can set it
to something else using the logout-url attribute. More information on
other available attributes may be found in the namespace appendix.
However, after following security setting in the doc, the URL /logout doesn't show logout page. Instead, it shows
On the contrary, the URL /login works properly.
The following is my setting:
Spring Framework 4.1.6
Spring Security 4.0.0
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Test8</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<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>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/security-config.xml</param-value>
</context-param>
</web-app>
security-config.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.xsd">
<http>
<intercept-url pattern="/**" access="hasRole('USER')" />
<form-login />
<logout />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="aaa" password="111" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bbb" password="222" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
Spring security automatically enables csrf, which automatically disabled GET logouts.
You can fix this by disabling csrf protection by settings <csrf disabled="true"/> in the <http> , or just using a POST.
See http://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/#csrf-logout
Simply, put the following code in the jsp where you want to have the logout-
<c:url var="logoutUrl" value="/j_spring_security_logout" />
<form action="${logoutUrl}" id="logout" method="post">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
</form>
Logout
Corresponding entry in the bean configuration file-
<security:logout logout-url="/j_spring_security_logout" logout-success-url="/whateverPageYouWant" invalidate-session="true" />
-This worked for me for spring-security-4.*
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//...
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
}
The logout url is "/j_spring_security_logout" so edit your view accordingly
CSRF is by default enabled which will require every POST request (which the logout is) to have a CSRF token. So either disable CSRF (which I will not recommend) or frame the logout inside a form with action as above logout url and a hidden input with CSRF token like this
Note that there is no "logout page". the /logout is Spring's endpoint, that let Spring know that the app asks to logout a user, so it invokes a specific handler.
After logging the user out, Spring redirects to another page, and you can configure the "default target" in your XML.
Add to Spring Security:
<logout
logout-success-url="/anonymous.html"
logout-url="/perform_logout"
delete-cookies="JSESSIONID" />
under http tag
With Spring security 4.2.13 I have a managed to make this work by navigating to the logout URL through a form submission (POST method) instead of using a link.
I replaced <p>Log out</p> with
<form name='f' action='${pageContext.request.contextPath}/logout' method='POST'>
<input name="logout" type="submit" value="Log out" />
<input name="${_csrf.parameterName}" type="hidden"
value="${_csrf.token}" />
</form>
in my view layer, which is a JSP page. This way you will get a button instead of a link.
(In older Spring versions the default logout URL was "/j_spring_security_logout".)
Related
I have a Spring based web application deployed on Tomcat 7. What I do to run it is copy the application.war archive into the webapps directory and start the server, so my application root is available under https://localhost:8443/application/.
Then I wanted to add Spring security (3.2.4) to it. My intention is to secure the entire application, not only parts of it. The Spring security configuration I have:
<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.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<http auto-config="true">
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="admin" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
And in web.xml:
<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>
It's a basic configuration which can be found in the official docs and many tutorials. Everything works fine, when I'm not authenticated Spring redirects me to it's default login page. I can then log in and can access the application until the session expires or I navigate to the logout url.
The problem begins to show up when I access the application root without the trailing slash: https://localhost:8443/application.
Spring fails to find the security context:
2016-07-19 17:48:36,650 DEBUG: security.web.FilterChainProxy - / at position 1 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2016-07-19 17:48:36,650 DEBUG: web.context.HttpSessionSecurityContextRepository - HttpSession returned null object for SPRING_SECURITY_CONTEXT
2016-07-19 17:48:36,650 DEBUG: web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade#53b1e801. A new one will be created.
I then get redirected to the login page, but after I log in I get redirected to the original URL without the slash, Spring fails again and I see the login form again.
Does anybody know why is this happening? My configuration is minimal, uses out of the box defaults. The use case is also very simple and it feels weird that this is not working as expected. I feel I'm missing something fundamental, either with Spring or Tomcat.
Any help will be appreciated, thanks.
The problem was with the JSESSIONID cookie path -- Tomcat set it automatically to /application/, so any requests accessing the application root without a trailing slash were treated as new users which required authentication. Setting the cookie path to /application solved the problem for me.
just try this
<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>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
<http auto-config="true">
<!-- my be this can`t match -->
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- add this for try -->
<intercept-url pattern="/" access="ROLE_USER" />
</http>
i create a webservice as below:
web.xml
<display-name>MyService</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml,/WEB-INF/spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<!-- Spring Security Filter -->
<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>
beans.xml
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<context:component-scan base-package="com.*" />
<jaxrs:server id="employeeService" address="/employeeservices">
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
</jaxrs:providers>
<jaxrs:serviceBeans>
<ref bean="empService" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="xml" value="application/xml" />
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
</jaxrs:server>
<bean id="empService" class="com.service.impl.EmployeeServiceImpl"/>
<bean id="employeeDao" class="com.dao.EmployeeDao"/>
spring-security.xml
<http auto-config="true" use-expressions="true" create-session="stateless" >
<csrf disabled="true"/>
<http-basic entry-point-ref="restAuthenticationEntryPoint"></http-basic>
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
</http>
<beans:bean id="userAuthorService" class="com.auth.UserAuthorService"/>
<beans:bean id="restAuthenticationEntryPoint" class="com.auth.UserBasicAuthenticationEntryPoint">
<beans:property name="realmName" value="Name Of Your Realm"/>
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="userAuthorService">
<password-encoder ref="bcryptPasswordEncoder"/>
</authentication-provider>
</authentication-manager>
<beans:bean id="bcryptPasswordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="5" />
</beans:bean>
<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/>
By this configuration, is it flow of request process? :
request -> Spring security(check authentication) -> cxf -> response.
Are there any issues in this configuration when I deploy it in the real sever? what is the standard configuration when using cxf and spring ?
when i call right uri:http://localhost:8089/MyService/rest/employeeservices/getemployeedetals?employeeId=004. its ok.
But call wrong uri: http://localhost:8089/MyService/rest/employeeservices/getemployeedetallll?employeeId=004. It throw exception:
WARNING: No operation matching request path "/MyService/rest/employeeservices/getemployeedetallll" is found, Relative Path: /getemployeedetal, HTTP Method: GET, ContentType: /, Accept: /,. Please enable FINE/TRACE log level for more details.
Jun 07, 2016 1:55:17 PM org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: javax.ws.rs.ClientErrorException
at org.apache.cxf.jaxrs.utils.SpecExceptions.toHttpException(SpecExceptions.java:110)
at org.apache.cxf.jaxrs.utils.ExceptionUtils.toHttpException(ExceptionUtils.java:149)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.findTargetMethod(JAXRSUtils.java:477)
So how can i filter the right uri before access controller class.
Please help me. Thank you.
By this configuration, is it flow of request process? : request ->
Spring security(check authentication) -> cxf -> response.
Yes, because J2EE filters are executed around J2EE servlets (before and after euqeust), so springSecurityFilterChain will be executed before CXFServlet
Are there any issues in this configuration when I deploy it in the
real sever?
This is not necessary with latests versions of CXF
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
Also you are catching in CXFServlet /rest/* but in auth *. Is this an issue? Do you need rest services without authentication? Is difficult to analyze issues if we do not know the context of your system. Detail your doubts and I could try to help
what is the standard configuration when using cxf and spring ?
There is no any recommended configuration, because CXF can be used with spring or standalone, jax-ws or jax-rs, as a server or acting as a client, with authentication executed via spring security or integrated with RequestContextFilter or inInterceptor in jax-rs server or even at global level in CXF bus.
If you are talking about the necessary modules to configure so CXF was executed with spring, you have the needed ones.
For authentication, you could consider use a JWT token instead of a bcrypt passwod.
when i call right
uri:http://localhost:8089/MyService/rest/employeeservices/getemployeedetals?employeeId=004.
its ok. But call wrong uri:
http://localhost:8089/MyService/rest/employeeservices/getemployeedetallll?employeeId=004.
It throw exception:
WARNING: No operation matching request path
This warning is normal. The server is responding with an http error code 404-Not found because there is no resource available at rest/employeeservices/getemployeedetallll
So how can i filter the right uri before access controller class.
CXF is doing for you by returning a 404 if the path is not found. It is the correct behaviour. Do you mean CXF uri filter be executed before spring-security? You can not with this configuration because auth filter is previous.
I have a web application protected by spring security. However, there is one particular URL pattern that I do not want to apply any security to. I've tried a few different approaches, but none of them seem to be working. Whenever I attempt to go to that specific URL, I am forwarded to the spring_security_login page.
A piece of the web.xml:
<!-- NOT SECURE -->
<servlet>
<servlet-name>dontSecureServlet</servlet-name>
<servlet-class>org.com.gov.lol.DontSecure</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dontSecureServlet</servlet-name>
<url-pattern>/dontSecure</url-pattern>
</servlet-mapping>
<!-- SECURE -->
<servlet>
<servlet-name>secureServlet</servlet-name>
<servlet-class>org.com.gov.lol.Secure</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>secureServlet</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<!-- SECURITY FILTER -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
A piece of the security-context.xml:
<!-- This did not work -->
<http pattern="/dontSecure" security="none" />
<http entry-point-ref="entryPoint" use-expressions="true">
<custom-filter ref="customFilter" position="PRE_AUTH_FILTER" />
<!-- I've also tried adding the following here (with no luck) -->
<intercept-url pattern="/dontSecure" filters="none" />
<intercept-url pattern="/secureMe" requires-channel="https" />
<intercept-url pattern="/secureUs" requires-channel="https" />
</http>
<!-- I have even tried adding a separate <http> block just for /dontSecure -->
<http>
<intercept-url pattern="/dontSecure" security="none" />
</http>
Again, with any combination of the above configuration, the url /dontSecure is still forwarded to the spring login page.
Any ideas as to what could be causing this?
Cheers.
UPDATE
From the server logs, it seems that the /dontSecure url is loading the dontSecureServlet. However, an error seems to be generated and I am being forwareded the 404 error page (which is configured as /404.htm, which must be what brings me back to the login page).
I've included some extra snippits from the web.xml that I didn't think were relevant at first.
Try to change your intercept-url in your configuration with this one:
<intercept-url pattern="/dontSecure/**" access="permitAll" />
not in a separate <http> tag
The problem might be caused by the fact that your custom filter is getting applied to all the paths. You have to find a way to register your custom filter only for certain paths (I could show you how to do this in Spring Boot, but you probably aren't using it).
I have a situation where we have two separate home pages for signed in and non-signed in users. But we are trying to cache these pages using cloudfront. One part of the home page is dynamic and we are trying to set this using an Ajax/jquery calling restful service and setting the output values in template javascript. This javascript is executed on load of signed in JSP.
I believe SecurityContextHolder.getContext().setAuthentication() is being called after logging in.
In Restful webservice called by Ajax I am trying to get Loggedin User using
SecurityContextHolder.getContext().getAuthentication().getPrincipal(). But getAuthentication() is returning null.
My web.xml is as below
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
WEB-INF/classes/persistence-context.xml
WEB-INF/xyz-servlet.xml
WEB-INF/classes/environment-context.xml
WEB-INF/classes/transactional-context.xml
WEB-INF/classes/spring-security-context.xml
WEB-INF/classes/services-context.xml
WEB-INF/classes/aop-context.xml
</param-value>
</context-param>
<!-- Spring Security Filter -->
<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>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
In spring-security-context.xml I have below values
<http use-expressions="true" auto-config="false" create-session="never" entry-point-ref="authenticationProcessingFilterEntryPoint">
<access-denied-handler ref="exceptionResolver" />
<intercept-url pattern="/xyz/myaccount/**" access="hasRole('FRONT_END_USER')" />
<intercept-url pattern="/xyz/form-clickref*" access="hasRole('FRONT_END_USER')" />
<intercept-url pattern="/**" filters="none" />
<form-login login-page="/xyz/login" login-processing-url="/xyz/j_spring_security_check"
authentication-failure-handler-ref="xyzAuthenticationFailureHandler"
authentication-success-handler-ref="simpleUrlAuthenticationSuccessHandler" />
<logout logout-url="/xyz/logout" invalidate-session="true" success-handler-ref="logoutSuccessHandler" />
<remember-me services-ref="rememberMeServices" key="fe2c667b-d39e-4277-ba66-709ca0bee944" />
<anonymous enabled="false" />
<request-cache ref="nullRequestCache" />
<session-management session-fixation-protection="none" />
</http>
I guess, the application lost track of the users http session in your AJAX Requests.
Normally there are two (three) ways to track the session of an user:
using JSessionID a url query parameter
using a cookie that contains the JSessionID
(mixed mode: the sever decides which of the two above techniques to use)
I guess that your AJAX request is not sending the Cookie nor the JSessionId request parameter.
To understand the problem, I would use some tool/Firefoxplugin (FireBug, Http Live Headers, ...) to have a look at the REQUESTs sended to the server. I would check the "normal" requests, and have a look whether I can find the JSessionID (url query paramter, or session). Then I would have a look at the AJAX requests, and would look for the same information.
If there is a JSessionId in the normal requests, but not in the AJAX requests, then my guess is right. Then all you need to do, is to this information to the AJAX requests too. good luck
I'm new to Java EE / Spring and I've been fighting with security for the past two days. I finally have to the point where I can authenticate with Java EE and authorize with Spring. Unfortunately, I can't access the Java EE authentication from Spring to do the authorization.
The general idea for the application is this:
If you're not logged in, Spring security redirects you to the login page
The login page is the only page that is locked by Java EE. It outputs a form for authentication
After successfully logging in, the user is redirected to the home page, which should have the authentication object associated with it.
Note the emphasis on should since it isn't. For whatever reason, the user is coming back as authenticated but anonymous. If I could simply get the username, I could construct the user details.
Here's the relevant snippets from my web.xml:
<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>
</filter>
<security-constraint>
<web-resource-collection>
<web-resource-name>Home Page Only</web-resource-name>
<description>Rely on Spring for Authorization</description>
<url-pattern>/login</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/loginfailed</form-error-page>
</form-login-config>
</login-config>
And my security-content.xml for spring:
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:lang="http://www.springframework.org/schema/lang"
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://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/about" access="permitAll"/>
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/loginfailed" access="permitAll"/>
<intercept-url pattern="/css/*" access="permitAll"/>
<intercept-url pattern="/resources/*" access="permitAll"/>
<intercept-url pattern="/images/*" access="permitAll"/>
<intercept-url pattern="/js/*" access="permitAll"/>
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login default-target-url='/index.jsp'
always-use-default-target='true' login-page="/login"
authentication-failure-url="/loginfailed"
/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="securityRef"/>
</authentication-manager>
<beans:bean id="securityRef"
class="my.custom.authprovider.AuthenticationProviderImpl"/>
</beans:beans>
Any ideas?
If you want to use Java EE authentication you should configure
PreAuthenticatedProcessingFilter (more precisely J2eePreAuthenticatedProcessingFilter).
Also, you need to configure J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource to populate a user granted authorities:
http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#j2ee-preauth-details
BTW, What advantages do you see to use Java EE Form authentication?
Added after the comment.
If required to use Java EE authentication your approach should be corrected: you should protect the whole application by Java EE authentication.
In this case each request that come to Spring Security will be authenticated by Java EE container and Spring Security can authenticate the request by J2eePreAuthenticatedProcessingFilter.
So the corrected flow
If a user is not logged in, Java EE container redirects the user to the login page
After a successfully authentication, the user is redirected to a page protected by Spring Security. Spring Security can authenticate the request by J2eePreAuthenticatedProcessingFilter.