I am moving from Spring Security 3.2 to 4.1, and I (still) use xml configuration.
It seems that using the <logout /> element does not allow setting the http method to GET.
Is this true?
If, yes, does it mean I have to create a Controller mapping to "/logout" and log out programmatically from there?
Due to legacy reasons, I have to use GET for my LogOutFilter and XML config. The below works. Note I don't recommend working around the csrf protection but I've had to.
This might help others.
<b:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<b:constructor-arg name="logoutSuccessUrl" value="/loggedOut" />
<b:constructor-arg name="handlers">
<b:list>
<b:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</b:list>
</b:constructor-arg>
<b:property name="logoutRequestMatcher">
<b:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<b:constructor-arg name="pattern" value="/logout*"/>
<b:constructor-arg name="httpMethod" value="GET"/>
</b:bean>
</b:property>
</b:bean>
Remember to put the custom filter in the element
<custom-filter before="CSRF_FILTER" ref="logoutFilter" />
The key thing here is that you register your own logoutRequestMatcher
I'm writing a RESTful web service that requires multiple authentication mechanisms (basic, x509, and anonymous). I therefore have three <http> elements in three separate spring context files.
When I start my service, I'm getting the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [org.springframework.security.web.SecurityFilterChain]
is defined: expected single matching bean but found 3:
org.springframework.security.web.DefaultSecurityFilterChain#0,
org.springframework.security.web.DefaultSecurityFilterChain#1,
org.springframework.security.web.DefaultSecurityFilterChain#2
I think this makes sense, right? I've defined three <http> elements, so spring is probably creating three instances of org.springframework.security.web.DefaultSecurityFilterChain. And now someone is asking for a bean of type org.springframework.security.web.SecurityFilterChain, and is finding three.
But, according to Spring Security documentation, this is supposed to be possible, so my question is: How do I get this scenario to work?
Here are my three <http> configurations:
x509Auth.xml:
<sec:http pattern="/service/x509/**" use-expressions="true">
<sec:x509 subject-principal-regex="(.*)" user-service-ref="ldapUserDetailsService" />
<sec:intercept-url pattern="/service/x509/identity/**" access="hasRole('Domain Users')" />
</sec:http>
basicAuth.xml:
<sec:http pattern="/anubis/basic/**" use-expressions="true" create-session="stateless">
<sec:intercept-url pattern="/service/basic/identity/**" access="isAuthenticated()" />
<sec:http-basic />
</sec:http>
noAuth.xml:
<sec:http pattern="/service/anonymous/**" security="none" />
Thanks to this InfoQ post, I learned that with new flexibility comes new responsibility. Since you can have multiple <http> elements now, you can also have multiple authentication managers. This requires us to tell spring which authentication manager goes with each<http> element.
Here is my now-working spring configuration:
<!-- This section configures X509 Certificate Authentication -->
<sec:http
pattern="/service/x509/**"
use-expressions="true"
authentication-manager-ref="ldapAuthenticationManager">
<sec:x509 subject-principal-regex="(.*)" user-service-ref="ldapUserDetailsService" />
<sec:intercept-url pattern="/service/x509/identity/**" access="hasRole('Domain Users')" />
</sec:http>
<sec:authentication-manager alias="ldapAuthenticationManager">
<sec:authentication-provider user-service-ref="ldapUserDetailsService" />
</sec:authentication-manager>
<!-- This section configures BASIC Authentication -->
<sec:http
pattern="/service/basic/**"
use-expressions="true"
create-session="stateless"
authentication-manager-ref="mongoAuthenticationManager">
<sec:http-basic />
<sec:intercept-url pattern="/service/basic/identity/**" access="isAuthenticated()" />
</sec:http>
<sec:authentication-manager alias="mongoAuthenticationManager">
<sec:authentication-provider user-service-ref="mongoUserDetailsService" />
</sec:authentication-manager>
<!-- This section configures NO Authentication -->
<sec:http pattern="/service/anonymous/**" security="none" />
I've setup an OAuth2 server with spring security. I want to write client application to use this oauth server with spring security without protecting any resource. Means I just want to run oauth2 from client side with spring security 3.1. I have written the following configuration but it asks for credentials before redirecting to oauth2 server authorize page. But I want to redirect user to oauth2 server authorization page before asking any credentials from client side. I am using following configuration
<http auto-config='true' xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/product/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<custom-filter ref="oauth2ClientFilter" after="EXCEPTION_TRANSLATION_FILTER" />
</http>
<authentication-manager xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="jimi" password="jimi" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<!--apply the oauth client context -->
<oauth:client id="oauth2ClientFilter" />
<oauth:resource id="fooClient" type="authorization_code"
client-id="foo" client-secret="secret" access-token-uri="${accessTokenUri}"
user-authorization-uri="${userAuthorizationUri}" scope="read" />
<bean id="dService" class="com.abc.service.DServiceImpl">
<property name="dURL" value="${dURL}"></property>
<property name="dRestTemplate">
<oauth:rest-template resource="fooClient" />
</property>
</bean>
So i just want /product url should access oauth2 server. Rest of the URL mapping should work without this.
And User should be anonymous for client ( No need to show login from on client side).
But When I run my application "http://localhost/client-sample/product/1" then it shows "http://localhost/client-sample/spring_security_login". But I want user should redirect to oaut2 server page.
Spring security prevents anonymous users from acquiring an access token. But if you still want this functionality in your application then you will have to extend org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails class and override isClientOnly() method.
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
public class ExtendedBaseOAuth2ProtectedResourceDetails extends
AuthorizationCodeResourceDetails {
public boolean isClientOnly() {
return true;
}
}
By default this method returns false. So you have to override this method to return true.
Then in your root-context.xml file you have to define oaut2 resource like this.
<bean id="fooClient" class="com.abc.service.ExtendedBaseOAuth2ProtectedResourceDetails">
<property name="clientId" value="foo"></property>
<property name="clientSecret" value="secret"></property>
<property name="accessTokenUri" value="${accessTokenUri}"></property>
<property name="userAuthorizationUri" value="${userAuthorizationUri}"></property>
<property name="scope" value="#{{'read','write'}}"> </property>
</bean>
<bean id="dService" class="com.abc.service.DServiceImpl">
<property name="dURL" value="${dURL}"></property>
<property name="dRestTemplate">
<oauth:rest-template resource="fooClient" />
</property>
</bean>
This will not ask authorization on client side before redirecting the user to the oauth2 provider authorization page.
I have a spring 3 application with the configurations given below. When any user tries to access a page and he/she isn't logged in, I get an Access is Denied exception with an ugly stack trace. How do I handle this exception and not let it dump out a stack trace. I implemented my own access-denied-handler but that doesn't get invoked.
Based on the type of the requested resource, I would like to show custom error messages or pages. Here is my spring configuration.
How do I get Spring to invoke my access-denied-handler . Here is my spring configuration
<security:http auto-config='true'>
<security:intercept-url pattern="/static/**" filters="none"/>
<security:intercept-url pattern="/login" filters="none"/>
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login login-page="/index"
default-target-url="/home" always-use-default-target="true"
authentication-success-handler-ref="AuthenticationSuccessHandler"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/index?error=true"/>
<security:remember-me key="myLongSecretCookieKey" token-validity-seconds="1296000"
data-source-ref="jdbcDataSource" user-service-ref="AppUserDetailsService" />
<security:access-denied-handler ref="myAccessDeniedHandler" />
</security:http>
<bean id="myAccessDeniedHandler"
class="web.exceptions.handlers.AccessDeniedExceptionHandler">
<property name="errorPage" value="/public/403.htm" />
</bean>
The custom class for handling this exception is given below
public class AccessDeniedExceptionHandler implements AccessDeniedHandler
{
private String errorPage;
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException arg2) throws IOException, ServletException {
response.sendRedirect(errorPage);
}
public void setErrorPage(String errorPage) {
if ((errorPage != null) && !errorPage.startsWith("/")) {
throw new IllegalArgumentException("errorPage must begin with '/'");
}
this.errorPage = errorPage;
}
}
When I run this application, this is the error that I get. I am only pasting a part of the stacktrace and the Spring Debug logs.
20:39:46,173 DEBUG AffirmativeBased:53 - Voter: org.springframework.security.access.vote.RoleVoter#5b7da0d1, returned: -1
20:39:46,173 DEBUG AffirmativeBased:53 - Voter: org.springframework.security.access.vote.AuthenticatedVoter#14c92844, returned: 0
20:39:46,178 DEBUG ExceptionTranslationFilter:154 - Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:71)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:204)
How do I fix this problem? Firstly, I want to stop spring from Throwing that exception. If it still throws it, I want to handle it and not raise any flags.
Update: I have attached a part of my web.xml as well.
<!-- Hibernate filter configuration -->
<filter>
<filter-name>HibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-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>
<!--Dispatcher Servlet -->
<servlet>
<servlet-name>rowz</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
In your configuration You require the user to be always authenticated when entering any URL on Your site:
<security:intercept-url pattern="/**" access="ROLE_USER" />
I think You should allow the user to be unauthenticated when entering the login page:
<security:intercept-url pattern="/your-login-page-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/your-login-process-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/your-login-failure-url" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/**" access="ROLE_USER" />
If You use URL's like: /login/start, /login/error and /login/failure You can have:
<security:intercept-url pattern="/login/**" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/**" access="ROLE_USER" />
Update:
Having this configuration should make the framework to redirect all unauthenticated (anonymous) users to login page, and all authenticated to AccessDeniedHandler. The AccessDeniedException is one of the core parts of the framework and ignoring it is not a good idea. It's hard to help more if You only provide parts of Your Spring Security configuration.
Be sure to read the JavaDoc for ExceptionTranslationFilter for detailed explanation of what exceptions are thrown by the framework, why and how are the handled by default.
If possible, try removing as many custom parts You added, like AuthenticationSuccessHandler, RememberMeAuthenticationFilter and AccessDeniedHandler and see if the problem pesist? Try to get the minimal congiuration and add new features step by step to see where the error comes from.
One important thing that You don't mention in Your question is what is the result of this error message? Do You get HTTP 500? Or HTTP 403? Or do You get redirected to login page?
If, as You mentioned in the question, the user is unauthenticated and he/she gets redirected to login page, than that's how it's intended to work. It looks like You get the error message logged by ExceptionTranslationFilter:172 only because You have DEBUG level set to Spring Security classes. If so, than that's also how it's intended to work, and if You don't want the error logged, than simply rise the logging level for Spring Secyruty classes.
Update 2:
The patterns with filters="none" must match the login-page, login-processing-url and authentication-failure-ur attributes set in <security:form-login /> to skip all SpringSecurity checks on pages that display the login page and process the logging in.
<security:http auto-config='true'>
<security:intercept-url pattern="/static/**" filters="none"/>
<security:intercept-url pattern="/index" filters="none"/>
<security:intercept-url pattern="/j_spring_security_check" filters="none"/>
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login login-page="/index"
default-target-url="/home" always-use-default-target="true"
authentication-success-handler-ref="AuthenticationSuccessHandler"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/index?error=true"/>
<security:remember-me key="myLongSecretCookieKey" token-validity-seconds="1296000"
data-source-ref="jdbcDataSource" user-service-ref="AppUserDetailsService" />
<security:access-denied-handler ref="myAccessDeniedHandler" />
</security:http>
AccessDeniedHandler is invoked when user is logged in and there is no permissions to resource (source here). If you want to handle request for login page when user is not logged in, just configure in security-context:
<http ... entry-point-ref="customAuthenticationEntryPoint">
And define customAuthenticationEntryPoint:
<beans:bean id="customAuthenticationEntryPoint" class="pl.wsiadamy.webapp.controller.util.CustomAuthenticationEntryPoint">
</beans:bean>
TIP, don't try to fight with ExceptionTranslationFilter.
I have tried to override org.springframework.security.web.access.ExceptionTranslationFilter, without effects:
<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<beans:property name="authenticationEntryPoint" ref="customAuthenticationEntryPoint"/>
<beans:property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</beans:bean>
<beans:bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<beans:property name="errorPage" value="/accessDenied.htm"/>
</beans:bean>
The ref="customAuthenticationEntryPoint" just didn't invoked.
I have added Spring Access denied page in follwing way:
Spring Frame Work: 3.1
Spring Security: 3.1, Java 1.5+
Entry in *-security.xml:
<security:access-denied-handler error-page="/<My Any error page controller name>" />
Example:
<security:access-denied-handler error-page="/accessDeniedPage.htm" />
Error page will always start with "/"
Entry for controller:
#Controller
public class RedirectAccessDenied {
#RequestMapping(value = "/accessDeniedPage.htm", method = RequestMethod.GET)
public String redirectAccessDenied(Model model) throws IOException, ServletException {
System.out.println("############### Redirect Access Denied Handler!");
return "403";
}
}
Here 403 is my JSP name.
Spring Security uses an AuthenticationEntryPoint object to decide what to do when a user requires authentication. You can create your own AuthenticationEntryPoint bean ( see javadoc ), and then set the entryPoint attribute in the http element:
<http entry-point-ref="entryPointBean" .... />
However, by default, the form-login element creates a LoginUrlAuthenticationEntryPoint which redirects all of your unauthenticated users to the login page, so you shouldn't have to do this yourself. In fact, the log you posted claims it is forwarding the user to the authentication entry point: "Access is denied (user is anonymous); redirecting to authentication entry point".
I wonder if the problem is that you turned off the filter chain for the login url. Instead of setting filters to none, which means spring security is bypassed entirely, try keeping the filters on but allowing unrestricted access like this:
<security:intercept-url pattern="/login" access="permitAll" />
If that still doesn't help, please post the rest of the log so we can see what happens after the request is transferred to the entry point.
Programmatically solution:
#Order(1)
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//
// ...
//
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl() {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
super.handle(request, response, accessDeniedException);
accessDeniedException.printStackTrace();
}
});
//
// ...
//
}
}
Can you check your web.xml is supporting forward request?
errorPage is a FORWARD request and mostly in web.xml we support REDIRECTS only. Just a thought else your code looks ok to me.
Edited
A different point of view and This is been taken from working code only.
Have a look at Authenticated Voter class
Disable the annotations
<global-method-security pre-post-annotations="disabled"
secured-annotations="disabled" access-decision-manager-ref="accessDecisionManager">
</global-method-security>
bypassing filters
<http auto-config="true" use-expressions="true"
access-decision-manager-ref="accessDecisionManager"
access-denied-page="/accessDenied">
<intercept-url pattern="/appsecurity/login.jsp" filters="none" />
<intercept-url pattern="/changePassword" filters="none" />
<intercept-url pattern="/pageNotFound" filters="none" />
<intercept-url pattern="/accessDenied" filters="none" />
<intercept-url pattern="/forgotPassword" filters="none" />
<intercept-url pattern="/**" filters="none" />
<form-login login-processing-url="/j_spring_security_check"
default-target-url="/home" login-page="/loginDetails"
authentication-failure-handler-ref="authenticationExceptionHandler"
authentication-failure-url="/?login_error=t" />
<logout logout-url="/j_spring_security_logout"
invalidate-session="true" logout-success-url="/" />
<remember-me />
<!-- Uncomment to limit the number of sessions a user can have -->
<session-management invalid-session-url="/">
<concurrency-control max-sessions="1"
error-if-maximum-exceeded="true" />
</session-management>
</http>
custom Decision Voter
<bean id="customVoter" class="xyz.appsecurity.helper.CustomDecisionVoter" />
Access Decision Manager
<!-- Define AccessDesisionManager as UnanimousBased -->
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
<property name="decisionVoters">
<list>
<ref bean="customVoter" />
<!-- <bean class="org.springframework.security.access.vote.RoleVoter"
/> -->
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</property>
</bean>
Authentiation Exception Handler
<bean id="authenticationExceptionHandler"
class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<property name="exceptionMappings">
<props>
<!-- /error.jsp -->
<prop
key="org.springframework.security.authentication.BadCredentialsException">/?login_error=t</prop>
<!-- /getnewpassword.jsp -->
<prop
key="org.springframework.security.authentication.CredentialsExpiredException">/changePassword</prop>
<!-- /lockedoutpage.jsp -->
<prop key="org.springframework.security.authentication.LockedException">/?login_error=t</prop>
<!-- /unauthorizeduser.jsp -->
<prop
key="org.springframework.security.authentication.DisabledException">/?login_error=t</prop>
</props>
</property>
</bean>
It looks like spring tries to redirect users who have not logged in to the login page, which is "/index", but that itself is a protected url.
The other possibility is, it tries to display /public/403.html, but that is again protected by security configuration.
Can you add the following entries and try?
<security:intercept-url pattern="/login" filters="none" />
<security:intercept-url pattern="/public/**" filters="none" />
I am using both Spring security and Spring i18n. This is my security config:
<security:http access-denied-page="/denied.htm">
<security:form-login login-page="/login.htm"
authentication-failure-url="/login.htm?login_error=true" />
<security:intercept-url pattern="/denied.htm" filters="none"/>
<security:intercept-url pattern="/login.htm*" filters="none"/>
<security:intercept-url pattern="/*" access="IS_AUTHENTICATED_FULLY" />
<security:logout/>
</security:http>
Besides that, I have set authenticationManager for database with MD5 encoding for password. Security work just fine. My i18n config is:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
It works fine with reading locales from web browser's HTTP request, but I want it to change locale if I click on the link on the page (adds ?lang=hr parameter to current page). So when I add this, locale doesn't change at all:
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang" />
</bean>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="en"/>
</bean>
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
<property name="interceptors">
<ref bean="localeChangeInterceptor" />
</property>
</bean>
So I have few questions.
Why the locale interception suddenly doesn't work and how to fix it?
How to read the current chosen locale for user's session from java class? I have java class where I need to fetch spring's message from message_en.properties or message_hr.properties file. Jasper report.
I need to add some interceptor (or something like that) to restrain user with default password only to work with /changePassword.htm page. What is the simplest solution?
Many thanks
Why the locale interception suddenly doesn't work and how to fix
it?
I guess: To "fix" you local interceptor, you should check, that the local interceptor can be invoked even if the user is not logged in.
_2. How to read the current chosen locale for user's session from java
class?
Use the RequestContext.getLocale() method.
#see http://static.springsource.org/spring/docs/2.0.x/reference/mvc.html#mvc-localeresolver
added
The best place (in design/architecure) to obtain the local form the request is the web controller. If you are using Spring 3.0 you can obtain the HttpServletRequest directly if you put an parameter of this type to your Controller Request Handler Method. But you have an better choise: just add a Local parameter to your controller handler method
#see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-arguments
_3. I need to add some interceptor (or something like that) to restrain user
with default password only to work
with /changePassword.htm page. What is
the simplest solution?
One way (may not the simplest, and a one that needs documentation) is to give a user with the default passwort not the full set of priveleges (ony the privileges that he need to set the new password), after chaning tha password, give the user the full set of privileges, which allow him to do all the other stuff.
Try registering localeChangeInterceptor this way. It worked for me.
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="lang"></property>
</bean>
</mvc:interceptors>