It seems that XML configuration and Java are not performing the same tasks in Spring Security, regarding the HTTP Basic configuration.
When using the following Java configuration:
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
A HttpBasicConfigurer is used in order to use a different EntryPoint, when the request HTTP header X-Requested-With is XMLHttpRequest.
When using the configuration
<s:http use-expressions="true" create-session="ifRequired">
<s:intercept-url pattern='/**' access='isAuthenticated()' />
<s:http-basic />
<s:http />
The HTTPBassicConfigurer is not used.
Anybody knows how to add it using the XML configuration ?
The final solution based on the remarks provided by the people in this post, is that it is not possible to use HTTPBasicConfigurer with XML configuration. But there are other ways to perform almost the same thing that is implemented now in HTTPBasicConfigurer. My final solution used is mainly based on the remarks provided by Lea:
<s:http use-expressions="true" create-session="ifRequired" >
<s:intercept-url pattern='/**' access='isAuthenticated()' />
<s:http-basic entry-point-ref="entryPoint" />
</s:http>
<bean id="entryPoint"
class="org.springframework.security.web.authentication
.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry key="hasHeader('X-Requested-With','XMLHttpRequest')"
value-ref="ajaxEntyPoint" />
</map>
</constructor-arg>
<property name="defaultEntryPoint" ref="defaultEntryPoint"/>
</bean>
<bean id="ajaxEntyPoint"
class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
<constructor-arg name="httpStatus"
value="#{T(org.springframework.http.HttpStatus).UNAUTHORIZED}"/>
</bean>
<bean id="defaultEntryPoint"
class="org.springframework.security.web.authentication.www
.BasicAuthenticationEntryPoint">
<property name="realmName" value="My webservices"/>
</bean>
Basic authentication parameters can be declared explicitly with basic authentication filter:
<security:http use-expressions="true" entry-point-ref="entryPoint" authentication-manager-ref="authManager">
<security:custom-filter ref="advancedBasicFilter" position="BASIC_AUTH_FILTER"/>
<security:intercept-url pattern="/info/**" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()"/>
</security:http>
<bean id="advancedBasicFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<constructor-arg name="authenticationEntryPoint" ref="entryPoint"/>
<constructor-arg name="authenticationManager" ref="authManager"/>
</bean>
<bean id="entryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="My Realm"/>
</bean>
<security:authentication-manager id="authManager">
<security:authentication-provider user-service-ref="myOwnUserService" />
</security:authentication-manager>
If you are using xml config with the namespace, you do not use a HTTPBasicConfigurer, but attributes of the <http-basic> tag.
Extract from Spring Security Reference Manual Appendix The security namespace about <http-basic> tag :
Attributes
authentication-details-source-ref : Reference to an AuthenticationDetailsSource which will be used by the authentication filter
entry-point-ref Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter
Related
We have the following spring security configuration:
<bean id="authenticationSuccessHandler" class="***.JsonAuthenticationSuccessHandler"/>
<bean id="logoutSuccessHandler" class="***.web.security.***UrlLogoutSuccessHandler">
<property name="redirectStrategy" ref="noRedirectStrategy"/>
</bean>
<bean id="authenticationFailureHandler"
class="***.web.security.***UrlAuthenticationFailureHandler"/>
<bean id="httpStatusEntryPoint" class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
<constructor-arg value="UNAUTHORIZED"/>
</bean>
<security:http auto-config="true" use-expressions="false" entry-point-ref="httpStatusEntryPoint">
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrentSessionFilter"/>
<security:form-login
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-handler-ref="authenticationFailureHandler"
/>
<security:intercept-url pattern="/api/**"/>
<security:anonymous enabled="false"/>
<security:logout logout-url="/logout" delete-cookies="JSESSIONID,sessionId"
success-handler-ref="logoutSuccessHandler"
/>
<security:csrf disabled="true"/>
<security:session-management session-authentication-strategy-ref="sessionAuthenticationStrategy"/>
</security:http>
<bean id="concurrentSessionFilter" class="***.***ConcurrentSessionFilter">
<constructor-arg ref="***SessionRegistry"/>
<constructor-arg ref="errorController"/>
</bean>
<bean id="sessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<ref bean="registerSessionAuthenticationStrategy"/>
<ref bean="concurrentSessionControlAuthenticationStrategy"/>
</list>
</constructor-arg>
</bean>
<bean id="registerSessionAuthenticationStrategy" class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg name="sessionRegistry" ref="***SessionRegistry" />
</bean>
<bean id="concurrentSessionControlAuthenticationStrategy" class="***.web.security.***ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg name="sessionRegistry" ref="***SessionRegistry" />
<constructor-arg name="logoutService" ref="logoutService"/>
<property name="maximumSessions" value="1" />
</bean>
<!-- enable spring security annotation processing -->
<security:global-method-security secured-annotations="enabled"/>
<bean id="***LdapAuthenticationProvider" class="***.web.***LdapAuthProvider">
<property name="url" value="${ldap.url}"/>
<property name="filter" value="${ldap.filter}"/>
<property name="domain" value="${ldap.domain}"/>
<property name="dn" value="${ldap.dn}"/>
<property name="ldapEnabled" value="${ldap.enable}"/>
</bean>
<security:authentication-manager>
<security:authentication-provider ref="***LdapAuthenticationProvider"/>
<security:authentication-provider user-service-ref="***UserDetailsService"/>
</security:authentication-manager>
<bean id="usersResource" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="/users.properties" />
</bean>
<util:property-path id="usersResourceFile" path="usersResource.file" />
<bean id="***UserDetailsService" class="***.web.security.***InMemoryUserDetailsManager">
<constructor-arg index="0" ref="usersResourceFile"/>
</bean>
I tried different ways But I can not find a way to exclude some specific URLs from authentication.
For example:
/api/url/available/without/login
should be available even user is not logged in.
P.S.
I have tried to apply this answer, but it doesn't work for me:
https://stackoverflow.com/a/5382178/2674303
UPD
I have tired
....
<bean id="httpStatusEntryPoint" class="org.springframework.security.web.authentication.HttpStatusEntryPoint">
<constructor-arg value="UNAUTHORIZED"/>
</bean>
<security:http pattern="/api/url/available/without/login" security="none"/>
<security:http auto-config="true" use-expressions="false" entry-point-ref="httpStatusEntryPoint">
....
but when I try to use - this url still locked and I get 401
because this code:
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
String name = authentication != null ? authentication.getName() : "";
throw new BadCredentialsException("Could not find user " + name);
}
throws exception
You just need to add a "default" http interceptor:
<security:http xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/" access="permitAll()"/>
<anonymous/>
<csrf disabled="true"/>
</security:http>
after your current security:http tag. It will handle all requests, which were not handled by the first http construction.
I build an application with GWT on client side (in future I rewrite interface to JS) and duplicate functionality by REST requests. Based on Spring framework.
Now I need to implement authorization.
Here is my security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
..........>
<security:http auto-config="false">
<security:form-login login-page="/login.html"/>
<security:intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/mainGWT.html**" access="ROLE_USER"/>
<security:remember-me services-ref="rememberMeServices" />
</security:http>
<bean id="userDetailsService" class="com.damintsev.servlet.UserDetailsServiceImpl"/>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<constructor-arg name="key" value="rock"/>
</bean>
<context:component-scan base-package="com.login"/>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="rock"/>
<property name="tokenValiditySeconds" value="5000"/>
<property name="alwaysRemember" value="true"/>
</bean>
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="providerManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="org.springframework.security.authentication.RememberMeAuthenticationProvider#0"/>
</list>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
</security:authentication-manager>
But rememberMe services didnt work. So I start to debug this and I found that rememberMeAuthenticationProvider initializes twise!!
At first time initializes with name org.springframework.security.authentication.RememberMeAuthenticationProvider#0 and strange (autogenerated maybe) key.
The second provider initializes with propertly name rememberMeAuthenticationProvider with propertly key.
Later there is a problem because key in TokenBasedRememberMeServices didnt match with rememberMeAuthenticationProvider.
But when I change name of bean to org.springframework.security.authentication.RememberMeAuthenticationProvider#0 it works fine.
What am I doing wrong ? Who initialize provider twise ?
<spring.version>3.2.3.RELEASE</spring.version>
Second part of question: I dont understand how set RememberMe cookie to client so I write class. And if from client I receive remember = true I call that methood. And It works. If You provide some exmples with remeber me + REST it will helps.
#Component
public class Security {
#Autowired
private RememberMeServices rememberMeServices;
public void remeberMe(HttpServletRequest request, HttpServletResponse response, Authentication authentication1) {
rememberMeServices.setAlwaysRemember(true);
rememberMeServices.loginSuccess(request, response, authentication1);
}
}
You have a bean called rememberMeAuthenticationProvider and you are also using the namespace remember-me element which will also create one, hence the duplicate. You've also declared a filter which won't be used unless you actually insert it into the filter chain.
Either remove the namespace element and declare all the beans fully yourself, or stick with the namespace and let it do the work. If you want to customize the RememberMeServices you can you can retain that and the services-ref as you have it, but it's not clear from your question why you need to customize things.
If a remember-me cookie is sent by the client then the server will process it regardless of the type of client. How it is set in the first place depends on the login mechanism. Form login processing will automatically invoke the RememberMeServices and set the cookie if appropriate.
I've implemented my own LogoutHandler and I'm trying to configure it in the spring security xml, but for some reason it's not being called on logout (the logout is successful, but my code isn't executed).
This is my security.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"
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.1.xsd">
<security:http use-expressions="true">
<security:intercept-url pattern="/logoutSuccess"
access="permitAll" />
<security:logout logout-url="/logout"
logout-success-url="/logoutSuccess" />
</security:http>
<bean id="logoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg index="0" value="/logoutSuccess" />
<constructor-arg index="1">
<list>
<bean id="securityContextLogoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
<bean id="myLogoutHandler" class="my.package.MyLogoutHandler" />
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout" />
</bean>
MyLogoutHandler - this is what I want to execute on logout, but it's not being called:
import org.springframework.security.web.authentication.logout.LogoutHandler;
public class MyLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
System.out.println("logout!");
}
}
Does anyone have any idea why it's not working? Thanks!
As you want to use your custom filter instead of spring security default log out filter, add this line to logout filter bean
<security:custom-filter position="LOGOUT_FILTER"/>
or add this line in your spring security config
<security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
Editted
<security:http use-expressions="true">
<security:intercept-url pattern="/logoutSuccess"
access="permitAll" />
<security:logout logout-url="/logout"
logout-success-url="/logoutSuccess" success-handler-ref="myLogoutHandler" />
</security:http>
<bean id="myLogoutHandler" class="my.package.MyLogoutHandler" />
Also you can implement LogoutSuccessHandler interface instead of LogoutHandler
Edit2
ok, so if you dont want to call your handler after logout is complete, remove logout tag and set everything in logout filter bean
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg index="0" value="/logoutSuccess" />
<constructor-arg index="1">
<list>
<bean id="securityContextLogoutHandler"
class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
<bean id="myLogoutHandler" class="my.package.MyLogoutHandler" />
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/logout" />
</bean>
And add <security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
I am trying to implement an application with Spring security and CAS, it works fine on localhost but when I try to access it from an outside machine it and the application needs authentication it redirect to localhost too.
meaning
I access the application using https://172.16.1.50:8443/isxannouncements/
and when it needs authentication it should go to https://172.16.1.50:8443/cas/login/
but instead it goes to https://localhost:8443/isxannouncements/
which ofcourse breaks the application flow.
my config is
security-cas.xml
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://localhost:8443/isxannouncements/login"/>
</bean>
<!--
Allows changing where the CAS Server and CAS Service are easily
by specifying System Arguments or replacing the values only in one place.
Could also use external properties file -->
<context:property-placeholder
system-properties-mode="OVERRIDE" properties-ref="environment"/>
<util:properties id="environment">
<prop key="cas.service.host">localhost:8443</prop>
<prop key="cas.server.host">localhost:8443</prop>
</util:properties>
<!-- sends to the CAS Server, must be in entry-point-ref of security.xml -->
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="serviceProperties" ref="serviceProperties"/>
<property name="loginUrl" value="https://localhost:8443/cas/login" />
</bean>
<!-- authenticates CAS tickets, must be in custom-filter of security.xml -->
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/login"/>
</bean>
<bean id="casAuthProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="ticketValidator" ref="ticketValidator"/>
<property name="serviceProperties" ref="serviceProperties"/>
<property name="key" value="isxannouncements"/>
<property name="authenticationUserDetailsService" ref="DBUserServiceDetails"/>
<property name="statelessTicketCache" ref="statelessTicketCache"/>
</bean>
<bean id="statelessTicketCache" class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
<property name="cache">
<bean class="net.sf.ehcache.Cache"
init-method="initialise" destroy-method="dispose">
<constructor-arg value="casTickets"/>
<constructor-arg value="50"/>
<constructor-arg value="true"/>
<constructor-arg value="false"/>
<constructor-arg value="3600"/>
<constructor-arg value="900"/>
</bean>
</property>
</bean>
<bean id="ticketValidator" class="org.jasig.cas.client.validation.Saml11TicketValidator">
<constructor-arg value="https://localhost:8443/cas" />
<property name="encoding" value="utf8" />
</bean>
<!-- Handles a Single Logout Request from the CAS Server must be in custom-filter of security.xml -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
and my security.xml
<security:http pattern="/resources/images" security="none"/>
<security:http use-expressions="true" entry-point-ref="casEntryPoint">
<security:intercept-url pattern="/login/*"
access="permitAll"/>
<security:intercept-url pattern="/resources/**"
access="permitAll"/>
<security:intercept-url pattern="/logout"
access="permitAll"/>
<security:intercept-url pattern="/errors/**"
access="permitAll"/>
<security:intercept-url pattern="/approve-announcement**"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/delete-announcement**"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/edit-announcement**"
access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/admin/**"
access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/META-INF"
access="hasRole('ROLE_USER')"/>
<security:access-denied-handler error-page="/errors/403"/>
<security:custom-filter ref="singleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="casFilter" position="CAS_FILTER"/>
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout"
logout-success-url="https://localhost:8443/cas/logout"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthProvider" />
</security:authentication-manager>
how to fix this ??
Ok I found a workaround but I did not test it properly,
Inspired by this answer I did the following
since I have two domains that the user might use to access my application, the only way to get the domain the user used was to get from the request and then return it to the security provider.
I created a bean named serviceProperties and used it instead of the serviceproperties of spring, and I overrided the method of getService to return the service based on the domain name that the user to access the application.
Then I made this bean available at Web Application Context, and I passed in the session, I had already extracted the domain from the Request and put it in the Session.
So when the CasAuthenticationEntryPoint tries to get the service, I pass the service URL that I created from the session appended to it is the service name.
We handle this with property files. Anything that is specific to a certain environment (i.e. your local machine versus your test server) should be in a property file.
For example, create properties files for each environment with something like this:
localhost.properties:
cas.service.url=http://localhost/login
test.properties:
cas.service.url=http://mytestserver/login
And then configure spring security with the value from the properties file instead of directly as you have it above:
Your build process would then have a target for each environment to shuffle the appropriate files into place in the final artifact.
CAS works under domain. so you should use cas.example.com and define it in the cas.properties
In one of my deployed EARs I have a security context like so:
<security:http auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">
<security:intercept-url pattern="/login" filters="none" />
<security:intercept-url pattern="/**" access="isAuthenticated() and permitAll" />
<security:custom-filter position="FORM_LOGIN_FILTER" ref="ssoFilter" />
</security:http>
<bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login"/>
</bean>
Now in a second EAR I want to redirect to the login page of the first one. Can this be done somehow without extending the LoginUrlAuthenticationEntryPoint ?
Because currently if I try an absolute URL in the second EAR security context:
<property name="loginFormUrl" value="http://host/ear1ContextPath/login"/>
I get redirected to: http://host/ear2contextPath/login.
Which is incorrect.
Thanks
I managed to do this by implementing my own UrlAuthenticationFaliureHandler and handling the absolute URLs programatically (just do a servlet redirect and then break the filter chain).
Then I could inject the new FailureHandler in my custom filter like this:
<bean id="absoluteUrlLoginFailureHandler" class="tld.company.AbsoluteUrlAuthenticationFaliureHandler">
<property name="defaultFailureUrl" value="http://company.domain.tld/Login.html"/>
</bean>
<bean id="absoluteUrlFilter" class="tld.company.MyUserPassFilter">
<property name="authenticationFailureHandler" ref="absoluteUrlLoginFailureHandler"/>
</bean>
And then I set that filter in the namespace configuration:
<http auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">
<security:intercept-url pattern="/**" access="isAuthenticated() and permitAll" />
<custom-filter position="FORM_LOGIN_FILTER" ref="absoluteUrlFilter" />
</http>
The <entry-point-ref> element seems to handle absolute URLs by default.