How to use a simple Spring Security AuthenticationProvider without logon page? - java

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>

Related

ERR_TOO_MANY_REDIRECTS in Spring security 4.0

I have created security for my rest services and I have used custom filter for authentication using "AbstractAuthenticationProcessingFilter" here are my configs.
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="personDetailsService">
<security:password-encoder base64="true" hash="md5">
<security:salt-source user-property="username"/>
</security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<security:http entry-point-ref="restAuthenticationEntryPoint"
use-expressions="true" auto-config="false" create-session="stateless" >
<security:custom-filter ref="myAppAuthenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER"/>
<security:intercept-url pattern="/my/rest/**" access="isAuthenticated()"/>
<security:anonymous enabled="false"/>
<security:logout />
</security:http>
<bean class="my.web.auth.AppTokenAuthenticationFilter"
id="myAppAuthenticationTokenProcessingFilter">
<constructor-arg ref="requestMatcher"></constructor-arg>
</bean>
<bean class="my.web.auth.PersonDetailService" id ="personDetailsService"></bean>
<bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher" id="requestMatcher">
<constructor-arg type="java.lang.String" value="/my/**"></constructor-arg>
</bean>
<bean class="my.web.auth.RestAuthenticationEntryPoint" id ="restAuthenticationEntryPoint">
</bean>
</beans>
With this configuration my requests or keep on forward to filter after my SimpleUrlAuthenticationSuccessHandler called. Even its not working with permitAll in intercept URL.

Spring security proxy issues

I have a general question. I have a web project written using Spring Security 3.2 and Spring 4. I deployed project in Tomcat 7.0. There are 2 roles in spring sec for project users: USER and COMPANY. When I log in from home computer (without any proxy), everything works fine. But if I login from my work computer (my computer is behind company proxy) my web application does not work properly, It cannot get localization or often it gives USER role to company account and etc. I looked for this issue in web, but cannot find any solutions. Hope anybody can figure out what can be the reason. Thanks in advance..
spring-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"
xmlns:p="http://www.springframework.org/schema/p"
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.0.xsd">
<bean id="securityExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" />
<security:global-method-security
pre-post-annotations="enabled">
<security:expression-handler ref="securityExpressionHandler" />
</security:global-method-security>
<security:http auto-config="false" use-expressions="true" access-denied-page="/login" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/account/register" access="permitAll"/>
<security:intercept-url pattern="/main" access="hasAnyRole('ROLE_USER, ROLE_COMPANY')"/>
<security:intercept-url pattern="/profile" access="hasAnyRole('ROLE_USER, ROLE_COMPANY')"/>
<security:intercept-url pattern="/wishlist" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/messagebox" access="hasAnyRole('ROLE_USER, ROLE_COMPANY')"/>
<security:intercept-url pattern="/settings" access="hasAnyRole('ROLE_USER, ROLE_COMPANY')"/>
<security:intercept-url pattern="/search" access="hasAnyRole('ROLE_USER, ROLE_COMPANY')"/>
<security:logout invalidate-session="true" logout-success-url="/login" logout-url="/logout" />
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
<security:custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER"/>
<security:session-management session-authentication-strategy-ref="sas" />
</security:http>
<bean id="authenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:sessionAuthenticationStrategy-ref="sas"
p:authenticationManager-ref="authenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler"/>
<bean id="customAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"
p:defaultFailureUrl="/login?fail=true" />
<!-- We just actually need to set the default target url here -->
<bean id="customAuthenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler"
p:defaultTargetUrl="/main" />
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login"/>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="customAuthenticationProvider" />
</security:authentication-manager>
<bean id="customAuthenticationProvider" class="service.CustomAuthenticationManager">
</bean>
<!-- A custom service where Spring will retrieve users and their corresponding access levels -->
<bean id="customUserDetailsService" class="service.CustomUserDetailsService"/>
<bean id="concurrencyFilter" class="filter.AzunisConcurrentSessionFilter"
p:sessionRegistry-ref="sessionRegistry"
p:expiredUrl="/login" />
<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"
p:maximumSessions="-1" p:exceptionIfMaximumExceeded="false" p:alwaysCreateSession="true">
<constructor-arg name="sessionRegistry" ref="sessionRegistry" />
</bean>
<!-- Maintains a registry of SessionInformation instances
See: http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/org/springframework/security/core/session/SessionRegistry.html -->
<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
I think this is the caching mechanism of the proxy. Let the login and landingpage site expiring with in your Response Header.

Spring security for both web services and users

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.

How to login from Java/Rest client into an app with spring security

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.

Spring logging in with any details

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.

Categories