We have a web application, and we're want to use spring security to secure it in 2 different ways:
1) Users that are authenticate using login form and have access to certain services.
2) Other services that are secured using digest authentication (user + password are passed in the request's header) - used by other webapps so there's no login form.
Each of these works on it's own, but we weren't able to get them to work in the same web app.
When we try to run a webapp with both xmls we get the following error:
A universal match pattern ('/**') is defined before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your <security:http> namespace or FilterChainProxy bean configuration
The security.xml for users:
<security:http use-expressions="true">
<security:intercept-url pattern="/user/login"
access="permitAll" />
...
<security:intercept-url pattern="/**"
access="isAuthenticated()" />
<security:form-login
authentication-success-handler-ref="userAuthenticationSuccessHandler" />
<security:logout logout-url="/user/logout"
logout-success-url="/demo/user/logoutSuccess" />
</security:http>
<bean id="bCryptPasswordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
ref="authenticationProvider">
</security:authentication-provider>
</security:authentication-manager>
The rest-security.xml for web services:
<security:http create-session="stateless"
entry-point-ref="digestEntryPoint">
<security:intercept-url pattern="/provider/**"
access="ROLE_WEBAPP" />
<security:http-basic />
<security:custom-filter ref="digestFilter"
after="BASIC_AUTH_FILTER" />
</security:http>
<bean id="digestFilter"
class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
<property name="userDetailsService" ref="webappDetailsServiceImpl" />
<property name="authenticationEntryPoint" ref="digestEntryPoint" />
</bean>
<bean id="digestEntryPoint"
class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
<property name="realmName" value="Contacts Realm via Digest Authentication" />
<property name="key" value="acegi" />
</bean>
<security:authentication-manager>
<security:authentication-provider
ref="restAuthenticationProvider">
</security:authentication-provider>
</security:authentication-manager>
Does anyone has experiences with this kind of scenario?
I found the solution here: https://blog.codecentric.de/en/2012/07/spring-security-two-security-realms-in-one-application/
This post details exactly what I wanted to do.
The trick seems to be adding pattern="/provider/**" to the rest http element. So the correct rest-security configuration is:
<security:http create-session="stateless"
entry-point-ref="digestEntryPoint" pattern="/provider/**"
use-expressions="true">
<security:intercept-url pattern="/provider/**"
access="isAuthenticated()" />
<security:http-basic />
<security:custom-filter ref="digestFilter"
after="BASIC_AUTH_FILTER" />
</security:http>
This might not be quite what you're looking for, but I would consider breaking the two apis apart. One for humans and one for web services.
We have our human interface right off the root context:
http(s)://your.website.com/...
We then have our web service interface off of the api context:
http(s)://your.website.com/api/v1/...
This makes it easy to handle the two different types of security you're looking for with spring.
The error message
A universal match pattern is defined before other patterns in the filter chain, causing them to be ignored.
is very concise.
You should check the order of bean definition files of your contextConfigLocation context parameter in your web.xml.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>security.xml,rest-security.xml</param-value>
</context-param>
should reproduce the above error message.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>rest-security.xml,security.xml</param-value>
</context-param>
should solve the issue.
Related
I'm new at spring security. I want to have two different login forms for two different type of users. I have a package called /admin which is contained my main project for system users and /portal for other users./portal users will work on their tenant and won't know anything about /admin and vice versa.Each user group has its own database too.
In spring-security.xml I defined two authentication managers,but when I login from both login forms it goes to 'AuthenticatingManager' but as I mentioned in xml file, for /portal users it should goes to PortalAuthenticatingManager. what shall I do? or what did I miss?
<security:http use-expressions="true" pattern="/portal/**" authentication-manager-ref="portalAuthMgr" access-denied-page="/unauthorized.jsp">
<form-login login-page="/plLogin.jsp" default-target-url="/portal/portal" />
<security:intercept-url pattern="/plLogin.jsp" access="permitAll"/>
<security:intercept-url pattern="/portal/**" access="hasRole('ROLE_PORTAL')" />
</security:http>
<security:http authentication-manager-ref="adminAuthMgr" access-denied-page="/unauthorized.jsp">
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
<security:form-login login-page="/login.jsp" authentication-failure-handler-ref="authenticationFailureHandler"/>
</security:http>
<security:authentication-manager id="adminAuthMgr">
<security:authentication-provider ref="produxAuthenticationProvider"/>
</security:authentication-manager>
<security:authentication-manager alias="portalAuthMgr">
<security:authentication-provider ref="portalAuthenticationProvider"/>
</security:authentication-manager>
<beans:bean id="produxAuthenticationProvider" class="com.spring.AuthenticatingManager">
</beans:bean>
<beans:bean id="portalAuthenticationProvider" class="com.spring.PortalAuthenticatingManager">
</beans:bean>
Your "portal" login form needs to post to a URL beginning with /portal/**, otherwise the login request will be handled by the second filter chain. It should work if you use/portal/j_spring_security_check
Note that you can also use the login-processing-url attribute on form-login element to control which URL the filter responds to. Using different URLs for each would avoid the issue where one accidentally processes a request meant for the other.
Our application is built with Spring (MVC, transactions, authentication, etc). We use LoginUrlAuthenticationEntryPoint for authentication (please see below for the whole spring security xml). The web client (jsp) uses j_spring_security_check form to login to this app. The app has REST API and the browser code (web client) make REST calls to the app. So far so good and everything works fine. We are writing code in Java to test the app - end-to-end testing using REST calls (similar to how the real client, web client in my case, invokes the app). I am using Apache's HttpClient on the tests side to make REST calls to the app. Do you know how to authenticate/login to the app from the Java-written tests code? Any guidance is much appreciated. Thanks, prams.
<security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled"/>
<security:http auto-config="false" use-expressions="true" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/login*" filters="none"/>
<security:intercept-url pattern="/agentMsg" filters="none"/>
<security:intercept-url pattern="/wait" filters="none"/>
<security:intercept-url pattern="/systemConfig/**" filters="none"/>
<security:intercept-url pattern="/js/**" filters="none" />
<security:intercept-url pattern="/css/**" filters="none" />
<security:intercept-url pattern="/images/**" filters="none" />
<security:intercept-url pattern="/heartbeat" filters="none" />
<security:intercept-url pattern="/reposTracking" filters="none" />
<security:intercept-url pattern="/alerts/sev" filters="none" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:form-login
login-page="/login"
default-target-url="/"
authentication-failure-url="/login?login_error=1"
authentication-success-handler-ref="mcLoginSuccessHandler"
authentication-failure-handler-ref="mcLoginFailureHandler"
/>
<security:remember-me/>
<security:logout success-handler-ref="mcLogoutHandler"/>
<security:custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter"/>
<security:session-management session-authentication-strategy-ref="sas"/>
</security:http>
<!-- needed for remember-me service -->
<bean id="customUserDetailService" class="com.mycompany.admin.tools.webui.beans.MyCompanyUserDetailsService"/>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="vuiAuthenticationProvider"/>
</security:authentication-manager>
<bean id="vuiAuthenticationProvider" class="com.mycompany.admin.tools.webui.beans.VuiMycompanyUserDetailsAuthenticationProvider">
<property name="userDetailsService" ref="customUserDetailService"/>
<property name="passwordEncoder" ref="md5PasswordEncoder"/>
</bean>
<bean id="md5PasswordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login"></property>
</bean>
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
<property name="sessionRegistry" ref="sessionRegistry"/>
<property name="expiredUrl" value="/login"/>
<!-- <property name="redirectStrategy" value=""></property> -->
</bean>
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
<property name="maximumSessions" value="-1" /> <!-- no limit on number of session per user -->
</bean>
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
<bean id="mcLogoutHandler" class="com.mycompany.admin.tools.webui.servlets.McLogoutHandler"/>
<bean id="mcLoginSuccessHandler" class="com.mycompany.admin.tools.webui.servlets.McLoginSuccessHandler"/>
<bean id="mcLoginFailureHandler" class="com.mycompany.admin.tools.webui.servlets.McLoginFailureHandler"/>
First of all, if your tests are making http requests then they are not unit tests, but functional tests.
If you want to make such tests work you will basically have to go through the steps that are performed when you log in from a browser: POST credentials to /j_spring_security_check, and make sure that the JSESSIONID cookie that was set in the response is sent back to the server with each subsequent call.
You should test one level lower. Test your business logic in unit test manner. Why do you need to test if RestEasy/CXF/Jersey/whatever is calling methods properly? Do you need to test if spring-security works fine? I guess someone has written unit-tests for that already.
For integration testing read Spring documentation. You can set-up a bit simpler context without authentication and call MockHttpRequest.
For REST testing you may also use SoapUI.
Does anyone know in Spring a way to implement a user-service or authentication provider that will accept any user when logging in?
This is further to my previous question: spring-ws get username & password
I have a basic security setup in my spring-ws project:
<security:http auto-config="true">
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:intercept-url pattern="/*.wsdl" access="ROLE_USER" />
<security:http-basic/>
</security:http>
<security:authentication-manager erase-credentials="false">
<security:authentication-provider user-service-ref="userService">
<security:user-service>
<security:user name="me" password="mypass"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
I don't want to specify users in the user-service, I want a user with any details to have access, I simply want to make sure the user gives a username and password and to ensure that I can access this from the SecurityContextHolder.
Is it necessary to implement my own user-service or authentication-provider to do this and if so can anyone point in the direction of an example or provide me with one?
Thanks!
You need to provide your own AuthenticationProvider i.e. an implementation of the org.springframework.security.authentication.AuthenticationProvider interface.
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="myProvider" />
</security:authentication-manager>
<bean id="myProvider" class="MyProvider"/>
MyProvider can then either delegate to a custom UserDetailsService to set the authorieties (the roles) or set a ROLE_USER directly.
if you change
<security:http auto-config="true">
to
<security:http auto-config="true" use-expressions="true">
you can then set your intercepts from
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:intercept-url pattern="/*.wsdl" access="ROLE_USER" />
to
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:intercept-url pattern="/*.wsdl" access="isAuthenticated()" />
that will allow any authenticated user access to those url patterns.
You need to do the following:
Configure your intercept URL to provide access to any role names. You can do this by doing the following:
<security:http auto-config="true">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_REMEMBERED" />
<security:intercept-url pattern="/*.wsdl" access="IS_AUTHENTICATED_REMEMBERED" />
<security:http-basic/>
</security:http>
Create your own user service which grants at least one authority to the user for any username and password combination.
Configure spring security to use the service you created.
<bean id="userService" class="com.ek.UserService" />
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userService" />
</authentication-manager>
I hope this gives you the idea on what you need to do. Else, we can work on creating a sample code for you.
I have a simple authentication provider that I'm trying to use with Spring Security.
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
<security:authentication-manager>
<security:authentication-provider
ref="ipAddressAuthenticationProvider" />
</security:authentication-manager>
Currently, with the above configuration, the user is redirected to a logon page when the first visit. I do not want this redirect. I'm trying to hit this authentication provider on every page visit. Any way to make this work without writing additional custom code?
I'm guessing I need to cleanly get rid of form filter and basic filter somehow.
Result
I got it working with the config below. I had to extend AbstractPreAuthenticatedProcessingFilter and simply return ""; for both of its abstract methods.
<security:http use-expressions="true" entry-point-ref="http403ForbiddenEntryPoint">
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:custom-filter position="PRE_AUTH_FILTER" ref="preAuthFilter" />
</security:http>
<bean id="preAuthFilter" class="com.hercules.ratinggame.business.security.IpAddressPreAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="http403ForbiddenEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider
ref="ipAddressAuthenticationProvider" />
</security:authentication-manager>
Currently you use auto-config="true" which means you get few filters configured iunder the hood, including <form-login> element with UsernamePasswordAuthenticationFilter filter.
Also, to hit this authentication provider on every page visit you'll need a filter which can obtain data from request (IP address as far as I can see). The filter will probably be RequestHeaderAuthenticationFilter or more likely your own AbstractPreAuthenticatedProcessingFilter implementation which will have access to your autentication-manager.
To sum up, configuration will look like:
<security:http use-expressions="true">
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:logout /> <!-- optional -->
<security:custom-filter position="PRE_AUTH_FILTER"
ref="ipFromRequestPreAuthenticationFilter" />
</security:http>
<!-- this will probably extend AbstractPreAuthenticatedProcessingFilter -->
<bean id="ipFromRequestPreAuthenticationFilter"
class="com.example.IpFromRequestPreAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="ipAddressAuthenticationProvider" />
</security:authentication-manager>
I am attempting to enable remember me functionality using spring security
<bean id="userService" class="mypath.service.UserDetailsServiceImpl" />
<security:http auto-config='true'>
<security:intercept-url pattern="/Login" filters="none" />
<security:form-login login-page='/Login' authentication-failure-url="/Login?login_error=1"/>
<security:remember-me data-source-ref="dataSource" />
</security:http>
However, I seem to have specify the userService somewhere in the remember-me element ? How do I do this.
The error I am getting on starting tomcat is
More than one UserDetailsService registered.
Please use a specific Id reference in <remember-me/> <openid-login/> or <x509 /> elements.
Ok it wasn't that hard, just in case anyway else finds the spring documentation rather challenging to navigate:
<security:remember-me data-source-ref="dataSource" user-service-ref="myUserService"/>