Spring Security 4: Allowing anonymous access and authenticated access on same url - spring

I have a Jersey 2 application which I'm trying to secure using Spring Security 4 and HTTP Basic authentication.
The controller class is a Spring bean and is injected into the Jersey Rest Resource. I'm using Spring Pre-Post-annotations on the controller's methods and tried out the following configs:
<security:global-method-security pre-post-annotations="enabled" />
<security:http>
<security:http-basic />
<security:intercept-url pattern="/**" access="hasRole('USER')" />
<security:csrf disabled="true"/>
</security:http>
Problem: Works fine with http-basic authenticated users, the annotated methods (#PreAuthorize("hasRole('USER')")) are secured and only work when the correct credentials are provided. BUT: Anonymous access on other resources under /** is not possible anymore (which is correct, but which I want to have).
Using
<security:intercept-url pattern="/**" access="permitAll" />
instead, every user is handled as anonymous, even though they are providing the correct credentials via HTTP Basic. So even a user with the correct role cannot access the annotated method, a AccessDeniedException is thrown.
What I want: Every user should be allowed to access all resources under "/**". If he's authenticated via HTTTP Basic and has sufficient rights, the annotated method can be called. If he's not authenticated or authorized, an AccessDeniedException should be thrown.
How do I have to configure the Spring security <http> section?

Related

Share Spring Security Context across multiple http sections

I have a situation where I have the basic application being served on the main endpoint (/**). I use spring security to authenticate the user before serving any further resources. The authentication is done with a login form.
But specifically the endpoint (/lti/**) is being used in a different Http Section. There, Spring security handles the authentication using OAuth.
After authentication on the /lti/** endpoint, the user is expected to load my entire application in an iFrame. This means access to all resources protected by the 1st Http-section.
Now the problem here is even though the user authenticated using OAuth creds for loading the app from /lti/**, and I set all appropriate user roles, when the application loads in the iFrame and tries to access any resources, it redirects to the login screen as directed by the 1st Http-section (The succeeding calls are through AJAX if that helps, but you can see the response in the dev tools).
Based on my research so far, the Security Context is not shared among different entry points. Different firewalls are instantiated so the Security context itself is re-initiated.
Here's a bit of my Security context:
<security:http pattern="/lti/**" use-expressions="true" entry-point-ref="oAuthProcessingFilterEntryPoint">
<security:headers>
<security:frame-options disabled="true"/>
</security:headers>
<security:intercept-url pattern="/**" access="hasRole('ROLE_OAUTH')"/>
<!-- Filter -->
<security:custom-filter ref="oAuthProcessingFilter" before="ANONYMOUS_FILTER"/>
<!-- Disable CSRF -->
<security:csrf disabled="true"/>
</security:http>
<security:http>
<security:intercept-url ... />
....
<security:form-login
authentication-success-handler-ref="authenticationSuccessHandler"
login-page=............"/>
<security:logout ................./>
<security:session-management invalid-session-url="............." session-authentication-error-url="................">
</security:session-management>
<!-- Enable csrf protection -->
<security:csrf/>
</security:http>
Any approach that will work or have any ideas how I ccan tackle the situation?
SecurityContext is populated in every request, the FilterChain set it up at the beginning of the request by using the SecurityContextPersistenceFilter. By deafult, this filter store the authentication data in the HttpSession.
The users are authenticated in a page an then you load an Iframe which starts a different HttpSession (you could check this if you watch the sessionids).
Then you have several options:
Do not use an Iframe. The best option, if it is not a must.
Configure a default RememberMe filter. the problem is that the users will be remembered in each session, until the cookie expires.
Store the SecurityContext in a more persistent way than the session.
Summarizing, I think that your problem is not about different entry points, its about different sessions.

Spring REST Basic Authentication Design Approach

Environment :
Spring 4
Spring Security 4
Spring MVC 4
Hibernate 4
MySQL
Issue :
Below is the requirement :
1)We are developing a Spring REST service for inventory management.
2)This web service will be consumed by .NET client. (or may be mobile device in future)
3)The users of REST service need to be authenticated. The user will use login form displayed by .NET client and if authentication is successfull , he will be
allowed to consume REST API.
4)If authentication fails , user won't be allowed entry into REST service.
Now we have decided to use Basic Authentication for this.
My question is : How do we achieve this using Spring MVC REST and Spring security ?
Below is my first attempt :
application-security.xml
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/usermanagement/authenticate" access="permitAll"/>
<intercept-url pattern="/abhishek/*" access="hasRole('ROLE_ADMIN')"/>
<http-basic/>
<csrf disabled="true"/>
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="Atul" password="12345" authorities="ROLE_ADMIN" />
</user-service>
</authentication-provider>
</authentication-manager>
Following is the flow happening right now :
1)The /authentication API looks up user in Db and returns Http Status code 201(success ) or 401 (failure) accordingly. (this url is unsecured)
2)If success , client puts username/password as Authorization header (which is used in login) and sends this header for future Http requests.
3)Now once next request comes (this is secured), spring security comes into picture and again authentication happens here.
But this time it will be Spring provided.
4)So there are two authentication mechanism are being used .
5)I know I am messing up here , but not able to decide on what is the correct approach to design this.
6)How can client be provided the authentication capability by hooking into Spring security ? He needs to know authentication success/failure
immediately after he logs in.
Please help since I am struggling a lot on this.

Spring Security custom filter registration with Java config

I am trying to setup pre-authentication authorisation using Spring Security, similar to site minder where an external system does the authentication and saves login information in a cookie. However for that to happen I need to redirect to the external URL.
I tried doing it from an implementation of AbstractPreAuthenticatedProcessingFilter but that doesn't work because the HttpServletResponse object is not available.
A more appropriate way seems to be to just add a custom filter that checks for cookie and does the redirection and once the cookies are available then passes the control forward to Spring Security filter. How can I register this custom filter in a Java configuration based Spring Security application? Any help would be appreciated.
The common way is to redirect user to the external authentication interface using an AuthenticationEntryPoint, for example LoginUrlAuthenticationEntryPoint. The entry point is automatically invoked by Spring Security whenever it determines that user needs to get authenticated.
Once user returns back to your application, it should hit a custom filter which extends the AbstractPreAuthenticatedProcessingFilter and extracts the username from your cookie/header/token (after perhaps some validity and integrity checks) in method getPreAuthenticatedPrincipal.
The Spring configuration could be similar to:
<security:http entry-point-ref="externalAuthentication">
<security:custom-filter after="BASIC_AUTH_FILTER" ref="cookieAuthentication"/>
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
</security:http>
<bean id="externalAuthentication" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="http://server.local/authenticationService"/>
</bean>
<bean id="cookieAuthentication" class="custom class extending org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter">
...
</bean>

Support SAML SSO and normal login

I have an application which is accessed by two types of users, internal and external.
I need to authenticate external users using SAML.
I need to authenticate internal users with the normal form-based login. My application need to support both types of users. I use spring security frame work.
Is it possible to support both types of users? if so can you suggest the approach at high level? Thanks.
You can easily enable support for both form and SAML authentication with configuration similar to this:
<http entry-point-ref="authenticationEntryPoint" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>
<form-login login-page="/login" />
<custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</http>
Make sure that your AuthenticationManager contains the samlAuthenticationProvider. And of course include other configuration parts from the Spring SAML sample application.
You can then create your custom login page which presents user with username+password fields for form-based authentication and a link/picture (or multiple of them) which initialize authentication with the IDP (by redirecting user to scheme://host:port/saml/login?idp=selectedIdpEntityId).
Your users then decide which one to use - depending on whether they's internal or external.
The part of Spring SAML documentation touching on this subject is in chapter Spring Security integration.

Basic Authentication Filter and authentication entry point not actually used from spring oauth2 token request

I have implemented resource owner flow with spring oauth2 based on spring's sparklr sample application and a couple of samples I found online. I tested the token request part with curl like this in order to provide both client and user credentials:
curl -v --data "username=user1&password=user1&client_id=client1&client_secret=client1&grant_type=password" -X POST "http://localhost:8080/samplerestspringoauth2/oauth/token"
and it works correctly, however I have made the following observation:
Although according to the examples I saw, I make use of the BasicAuthentication filter, this is not really used in the security process. Since the token request does not contain an Authentication header, the BasicAuthentication filter just skips doing any checks. ClientCredentialsTokenEndpointFilter and authentication-server are the only ones performing security checks during the token request. After noticing this and verifying it via debugging, I tried to remove completely the following part:
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
from the configuration. But then I got the warning:
"No AuthenticationEntryPoint could be established. Please make sure
you have a login mechanism configured through the namespace (such as
form-login) or specify a custom AuthenticationEntryPoint with the
'entry-point-ref' attribute".
As a next step, I added the entry-point-ref="clientAuthenticationEntryPoint in the http namespace, and got rid of the warning. I tested the app and played correctly.
However, in addition to the above, I have also made the following observation during debugging:
The ClientCredentialsTokenEndpointFilter, contains its own OAuth2AuthenticationEntryPoint entry point inside a private variable, and uses that when failing due to wrong client credentials.
Therefore, it does not matter what entry point I specify either in the basic filter, or in the http namespace. At the end ClientCredentialsTokenEndpointFilter will use its own private OAuth2AuthenticationEntryPoint.
To summarize my conclusions seem to be the following:
The basic filter is not used and can be removed, if we specify the
endpoint in the http namespace instead.
Specifying either a basic
filter,or an endpoint in http namespace is needed only for the
compiler to stop the warning. They have no practical use, and the
endpoint used is hardcoded inside
ClientCredentialsTokenEndpointFilter.
Below I put the http and endpoint configuration for the token request for your reference. I skip the rest of configuration for keeping the post easy to read:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter"
before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
I also assume that the same issue also occurs in the original sparklr application (which is spring oauth2 sample app) configuration for token request which is very similar. That can be found in https://github.com/spring-projects/spring-security-oauth/blob/master/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml, and the related part is below:
<http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/**" method="GET" access="ROLE_DENY" />
<intercept-url pattern="/**" method="PUT" access="ROLE_DENY" />
<intercept-url pattern="/**" method="DELETE" access="ROLE_DENY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<!-- include this only if you need to authenticate clients via request
parameters -->
<custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
I would expect spring oauth2 to more appropriately interact with spring security instead of having to put unnecessary and misleading configuration, and that makes me think that I may have missed something. Since security is a sensitive aspect I wanted to share that with you and ask if my conclusion correct.
The /oauth/token provides two different ways to authenticate clients which are requesting tokens:
Using HTTP-Basic authentication (when "http-basic" element is present)
The authentication is handled with org.springframework.security.web.authentication.www.BasicAuthenticationFilter and processes the "Authorization" HTTP header which contains base64 encoded credentials of the client. The filter only performs processing when the Authorization header is present. This method is always tried first. The entry point defined on http-basic will only be invoked when user has supplied an "Authorization" header with invalid content - that's why you don't see the entry point invoked in your debugger, try to set an Authorization HTTP header and your breakpoint will get a hit.
As defined in the OAuth standard using client_id and client_secret HTTP paremeters
This is handled using org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter and by default uses entry point which sends back WWW-Authenticate header to the client. The default entry point can be customized (there's a setAuthenticationEntryPoint method). The entry point is only used when you supply client_id parameter.
Both of these methods use different ways to obtain client's username+password, but verify it against the same authentication manager.
The "No AuthenticationEntryPoint could be established" error which you observe when taking out the <http-basic> element is coming from Spring Security itself, not from the OAuth Extension. The reason is that Spring Security is not able to tell that there's a default entry point already configured inside of the custom filter ClientCredentialsTokenEndpointFilter. And the HTTP configuration of Spring Security always must have at least one entry point available.
So, the complete logic goes as follows:
when you include "Authorization" header with invalid credentials and <http-basic> element is present , system will use entry point defined on the <http-basic> element. If none is specified (attribute entry-point-ref is missing), system will create a default instance of BasicAuthenticationEntryPoint automatically for you and use it.
when you include HTTP parameter "client_id" and "client_secret" with invalid credentials and custom filter clientCredentialsTokenEndpointFilter is present, system will use entry point defined in the clientCredentialsTokenEndpointFilter bean (which is by default instance of OAuth2AuthenticationEntryPoint)
in case neither "Authorization" header nor "client_id" parameter are present and the endpoint requires authentication ("IS_AUTHENTICATED_FULLY"), system will use the entry point defined on the <http entry-point-ref="">, if present, otherwise it will use the entry point defined on the http-basic (as above)
in case you don't specify neither http-basic (or other default authentication method which Spring recognizes), nor default entry point using the <http entry-point-ref="">, system will fail with "No AuthenticationEntryPoint could be established", because it requires at least one entry point and it doesn't understand that there's one available inside the clientCredentialsTokenEndpointFilter.
Regarding your observations:
>> The basic filter is not used and can be removed, if we specify the
endpoint in the http namespace instead.
> This is true in case you are authentication your clients using client_id + client_secret
>> Specifying either a basic filter,or an endpoint in http namespace is
needed only for the compiler to stop the warning. They have no
practical use, and the endpoint used is hardcoded inside
ClientCredentialsTokenEndpointFilter.
> Partly true, as the entry point will be used in case client_id is missing.
The configuration is indeed confusing (which is partly caused by the fact that OAuth isn't a native part of Spring Security, but an extension), but all of those settings make sense and are used in specific situations.
The changes you made have no security implications.

Resources