I'm new to Spring Security and Oauth, and I'm trying to set up a simple example protecting the access to resources in path "/api" with Oauth2. I'm using spring-security-oauth2-1.0.0.RC2. After some time dealing with configuration, I'm able to get tokens, but when I try to send requests to "/api" resources, I'm facing two questions:
Initially, I'm sending authorization header with "OAuth2" prefix, but spring-security-oauth2 seems to need headers with "Bearer" prefix in the tokens to find them. What's the difference between these tokens?
After Spring validated the token, I'm getting a security error: "ExceptionTranslationFilter - Access is denied (user is not anonymous)" and I'm stuck with this problem. Since I'm using InMemory token store, I have to login each time to authorize client, then I'm getting this error. Here is the spring configuration:
<http pattern="/api/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"
access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/api/**" access="ROLE_CLIENT,SCOPE_READ" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
<http disable-url-rewriting="true" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/**" access="ROLE_USER" />
<!--intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /-->
<form-login/>
<logout logout-success-url="/index.jsp" logout-url="/logout" />
</http>
<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="O2Server" />
</bean>
<bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="test" password="test" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="userApprovalHandler" class="org.o2server.security.O2ServerUserApprovalHandler">
<property name="tokenServices" ref="tokenServices" />
</bean>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" user-approval-handler-ref="userApprovalHandler">
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter" resource-id="O2Server" token-services-ref="tokenServices" />
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="O2Client" resource-ids="O2Server" authorized-grant-types="authorization_code,refresh_token,implicit"
authorities="ROLE_CLIENT" scope="read,write" secret="secret" />
</oauth:client-details-service>
<oauth:web-expression-handler id="oauthWebExpressionHandler" />
This is the log from the server:
11:58:30.366 [DEBUG] FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /api/user/; Attributes: [ROLE_CLIENT, SCOPE_READ]
11:58:30.366 [DEBUG] FilterSecurityInterceptor - Previously Authenticated: org.springframework.security.oauth2.provider.OAuth2Authentication#48a94464: Principal: org.springframework.security.core.userdetails.User#346448: Username: paul; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails#43794494; Granted Authorities: ROLE_USER
11:58:30.366 [DEBUG] UnanimousBased - Voter: org.springframework.security.oauth2.provider.vote.ScopeVoter#4e857327, returned: 0
11:58:30.366 [DEBUG] UnanimousBased - Voter: org.springframework.security.access.vote.RoleVoter#1b4b2db7, returned: -1
11:58:30.367 [DEBUG] ExceptionTranslationFilter - Access is denied (user is not anonymous); delegating to AccessDeniedHandler <org.springframework.security.access.AccessDeniedException: Access is denied>org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:90)
Please, could you give me some advice?
Thank you in advance.
I have never used oauth plugin but from reading debug output I can say that RoleVoter returned -1, which means access was denied by DecisionVoter. I see that you granted only ROLE_USER to principal which is trying to access url /api/user/, which is secured by [ROLE_CLIENT, SCOPE_READ] and that's the reason why access was denied.
Either grant ROLE_CLIENT and SCOPE_READ to principal or change <intercept-url pattern="/api/**" access="ROLE_CLIENT,SCOPE_READ" />.
As Xaerxess said, you should change your intercept-url access property to ROLE_USER.
<intercept-url pattern="/api/**" access="ROLE_CLIENT,SCOPE_READ" />
to
<intercept-url pattern="/api/**" access="ROLE_USER,SCOPE_READ" />
The access property controls the role of the user (which the client acts on behalf of).
The documentation of the authorities (ROLE_CLIENT) property of tag <oauth:client> states:
Authorities that are granted to the client (comma-separated). Distinct
from the authorities granted to the user on behalf of whom the client
is acting.
Also, according to user Dave Syer:
If you didn't change Tonr, it is not acting as a client, it is acting
on behalf of a user (who will not have the required ROLE_CLIENT), so
it is expected that you would get a 403. If you want to make an
assertion about the request from tonr being from a client with a
specific role (without changing the app) you can use an expression,
e.g. "oauthClientHasRole('ROLE_CLIENT') and hasScope('trust')".
Source:
http://forum.springsource.org/showthread.php?125468-confusing-between-ROLE_USER-ROLE_CLIENT
Related
I am new to Spring Security OAuth2 using version 2.0.10.RELEASE implementation. I developed code using 'InMemoryTokenStore' and I'm impressed with the way it works (it creates access_token, 'refresh_token' etc..), but I don't have enough understanding on how it works yet. Can anyone please help to know / provide understanding on how it works?
Is 'InMemoryTokenStore' the safest implementation from hacking perspective? I also see there are many implementation provided by OAuth2 like JdbcTokenStore, JwtTokenStore,KeyStoreKeyFactory. I don't think storing access_token into the database in the great idea like JdbcTokenStore does.
Which Implementation we should follow and why ?
spring-security-oauth2.xml file
<?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:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
<http pattern="/oauth/token" auto-config="true" use-expressions="true" create-session="stateless" authentication-manager-ref="authenticationManager"
xmlns="http://www.springframework.org/schema/security" >
<!-- <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> -->
<intercept-url pattern="/oauth/token" access="permitAll" />
<anonymous enabled="false" />
<http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<!-- Added this to fix error -->
<sec:csrf disabled="true" />
</http>
<http pattern="/resources/**" auto-config="true" use-expressions="true" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/resources/**" method="GET" />
<!-- <intercept-url pattern="/resources/**" access="IS_AUTHENTICATED_FULLY" /> -->
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<!-- Added this to fix error -->
<sec:csrf disabled="true" />
</http>
<http pattern="/logout" create-session="never" auto-config="true" use-expressions="true"
entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/logout" method="GET" />
<sec:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<!-- Added this to fix error -->
<sec:csrf disabled="true" />
</http>
<bean id="logoutSuccessHandler" class="demo.oauth2.authentication.security.LogoutImpl" >
<property name="tokenstore" ref="tokenStore"></property>
</bean>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
</bean>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<authentication-manager alias="authenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="clientDetails" class="demo.oauth2.authentication.security.ClientDetailsServiceImpl"/>
<authentication-manager id="userAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<authentication-provider ref="customUserAuthenticationProvider">
</authentication-provider>
</authentication-manager>
<bean id="customUserAuthenticationProvider"
class="demo.oauth2.authentication.security.CustomUserAuthenticationProvider">
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code />
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials />
<oauth:password authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
resource-id="springsec" token-services-ref="tokenServices" />
<!-- <bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" /> -->
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="300000"></property>
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<mvc:annotation-driven /> <!-- Declares explicit support for annotation-driven MVC controllers #RequestMapping, #Controller -->
<mvc:default-servlet-handler />
<bean id="MyResource" class="demo.oauth2.authentication.resources.MyResource"></bean>
</beans>
You're mixing in several things together. InMemoryTokenStore, JwtTokenStore and JdbcTokenStore are only supposed to be used for different cases. There is no such a thing which of them is safer and which is not.
JwtTokenStore
JwtTokenStore encodes token-related data into the token itself. It does not make tokens persistent and requires JwtAccessTokenConverter as a translator between a JWT-encoded token
and OAuth authentication information. ("Spring Essentials" by Shameer Kunjumohamed, Hamidreza Sattari).
The important thing is that tokens are not persisted at all and validated "on the fly" based on signature.
One disadvantage is that you can't easily revoke an access token, so they normally are granted with short expiry and the revocation is handled at the refresh token. Another disadvantage is that the tokens can get quite large if you are storing a lot of user credential information in them. The JwtTokenStore is not really a "store" in the sense that it doesn't persist any data. read more
InMemoryTokenStore
InMemoryTokenStore stores tokens in server memory so it's hardly possible to share them among different servers. You'll lose all access tokens in InMemoryTokenStore when you restart your authorisation server. I'd prefer to use InMemoryTokenStore only during development and not in a production environment.
The default InMemoryTokenStore is perfectly fine for a single server (i.e. low traffic and no hot swap to a backup server in the case of failure). Most projects can start here, and maybe operate this way in development mode, to make it easy to start a server with no dependencies. read more
JdbcTokenStore
The JdbcTokenStore is the JDBC version of the same thing, which stores token data in a relational database. Use the JDBC version if you can share a database between servers, either scaled up instances of the same server if there is only one, or the Authorization and Resources Servers if there are multiple components. To use the JdbcTokenStore you need "spring-jdbc" on the classpath. read more
In case of JdbcTokenStore you're saving the tokens in real database. So you're safe in case of Authorization service restart. The tokens can be also easily shared among the servers and revoked. But you have more dependancies for database.
I am using siteminder for authentication but currently for development purpose I am faking the URL attributes like SM_USER through fiddler tool. I am able to get the attribute in my class where I have Implemented my custom methods to get the user Permissions from DB. Everything works fine,at the end it redirects to the Spring Security Login Page.Below is my code snippet...
<http pattern="/pages/UnAuthorized.jsf*" security="none"/>
<http pattern="/pages/Logout.jsf*" security="none"/>
<http pattern="/pages/SessionTimeout.jsf*" security="none"/>
<http auto-config="false" use-expressions="true">
<intercept-url pattern="/**" access="fullyAuthenticated" />
<custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
<logout logout-url="/logout" logout-success-url="/pages/Logout.jsf" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider" >
</authentication-provider>
</authentication-manager>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="UserPermissionsProcessor"/>
</bean>
<bean id="customAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" ref="userDetailsServiceWrapper">
</property>
</bean>
I tried using entry-point-ref as suggested it in one of the post but didn't worked for me.
Is it creating the Authentication Object? If yes set that in SecurityContextHolder as follows.
SecurityContextHolder.getContext().setAuthentication(authentication);
If authentication object is not present, then the below line fails,
and obviously you will be redirected to the login page.
I try to implement my own example based on official tutorial Sparklr2/Tonr2. Everything looks good but when I remove from web.xml in my Tonr2 implementation, spring security filter I have exception:
No redirect URI has been established for the current request
I can't understand what URL should I use. Here is my code, for client implementation:
<!--apply the oauth client context -->
<oauth:client id="oauth2ClientFilter" />
<!--define an oauth 2 resource for sparklr -->
<oauth:resource id="provider" type="authorization_code" client-id="client" client-secret="secret"
access-token-uri="http://localhost:8080/provider/oauth/token" user-authorization-uri="http://localhost:8080/provider/oauth/authorize" scope="read,write" />
<beans:bean id="clientController" class="com.aouth.client.ClientController">
<beans:property name="trustedClientRestTemplate">
<oauth:rest-template resource="provider" />
</beans:property>
</beans:bean>
And for provider:
<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 />
</http>
<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>
<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling
separately. This isn't mandatory, but it makes it easier to control the behaviour. -->
<http pattern="/secured" create-session="never" access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false" />
<intercept-url pattern="/secured" access="ROLE_USER,SCOPE_READ" />
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<http-basic />
</http>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans">
<constructor-arg>
<list>
<bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
<bean class="org.springframework.security.access.vote.RoleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
</list>
</constructor-arg>
</bean>
<oauth:resource-server id="resourceServerFilter" resource-id="resource" token-services-ref="tokenServices" />
<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="clientDetailsService" ref="clientDetails"/>
</bean>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<http auto-config="true" xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/test" access="ROLE_USER" />
<intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
<authentication-provider>
<user-service>
<user name="pr" password="pr" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" >
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password />
</oauth:authorization-server>
<oauth:client-details-service id="clientDetails">
<oauth:client client-id="client" resource-ids="resource" authorized-grant-types="authorization_code, implicit"
authorities="ROLE_CLIENT" scope="read,write" secret="secret" />
</oauth:client-details-service>
I just want my client to work without spring security. And when I need my protected resource I want to login only on provider side.
You 2nd XML that you pasted here is the spring's XML for the oauth-provider and the protected-resource, which in your case run in the same webapp. (you can separate them, of course, if you wish).
The client (the 1st pasted-XML) is a different story. If I understand you correctly, you want your client to run without Spring's help (to be a regular webapp, and not spring-security-oauth-client webapp).
You have to understand how oAuth works: the client tries to get to a protected resource; if it does not have the access-token, it is being redirected to the oAuth-provider (that shows the login page and supplies the token). By the standard, the request for the access-token MUST contain a "redirect-uri" param, so after a successful login, the oAuth-provider knows where to redirect the client to. The oAuth client does it for you, and if you delete the "oauth client" from your web.xml, you now have to implement this by yourself.
Thanks for your answer. But I still don't understand how spring
security influences my oAuth client. And can I use for client side
spring-oauth (spring-mvc) without spring-security?
When you write this line in your XML:
< oauth:client id="oauth2ClientFilter" />
it means that you use spring-security-oauth, which is a package dedicated for oauth, built on spring-security. If you dig in, it puts a special filter (OAuth2ClientContextFilter) in the chain that handles the oAuth stuff, that are relevant for the client. One of them is sending the request with all the params ("redirect-uri" is one of them).
If you decide NOT to use spring-security-oauth, well - you will have to implement this logic by yourself...
Hope that helps!
I am trying to configure spring security 3 so that when a users is forced to login again (eg. when the session has timed out) the user will be taken back the the page they where on before the authorisation process began.
I am using a SavedRequestAwareAuthenticationSuccessHandler but the original request does not seem to be saved in the cache.
Below is my security configuration.
<security:http auto-config="false"
use-expressions="true"
access-denied-page="/views/auth/login?error=true"
entry-point-ref="authenticationEntryPoint" >
<security:intercept-url pattern="/*" access="hasRole('ROLE_USER')" />
<security:intercept-url pattern="/views/*" access="hasRole('ROLE_USER')" />
<security:intercept-url pattern="/data/*" access="hasRole('ROLE_USER')" />
<security:intercept-url pattern="/auth/*" access="permitAll" />
<security:logout invalidate-session="true" logout-success-url="/views/auth/login" logout-url="/views/auth/logout" />
<security:session-management invalid-session-url="/views/auth/login" >
<security:concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
</security:http>
<security:authentication-manager />
<bean id="authenticationFilter" class="com.security.web.filter.UsernamePasswordAuthenticationFilter">
<property name="allowSessionCreation" value="true" />
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureHandler" ref="aAuthenticationFailureHandler" />
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler" />
</bean>
<bean id="authenticationManager" class="com.security.web.manager.AuthenticationManager" />
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint" >
<property name="loginFormUrl" value="/views/auth/login"/>
</bean>
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/views/auth/login?error=true"/>
</bean>
<bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/views"/>
</bean>
Any help would be appreciated.
I know this question is old, but I wanted to document what I found for others.
Short Answer
The problem is that if you use the invalid-session-url on the <session-management> tag, then the original request will not be saved in the cache.
Explanation
This is discussed at the spring source forum:
http://forum.springsource.org/showthread.php?89352-requestCache-null-when-using-session-management-gt-invalid-session-url
In the above forum, Luke Taylor, a senior member of the security team says:
The invalid-session-url implies that the previous session has expired and the user should start again. There's no connection with the RequestCache which is used for restoring a request after a user has logged in.
and
When a session expires you can't know that it is safe to continue with the requested URL as some required state may have been lost along with the session. It may be OK in your particular case, but it's not a safe assumption in general.
You may be missing this :
<bean id="requestCacheAwareFilter"
class="org.springframework.security.web.savedrequest.RequestCacheAwareFilter">
<constructor-arg ref="requestCache"/>
</bean>
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache"/>
also you need to add requestCache been to authenticationSuccessHandler
<property name="requestCache" ref="requestCache"/>
I would like to base my Spring Security configuration depending on the user's context path. If the user goes against a url with http://path1/resource1 I would like to direct them to a specific authentication provider. If they come in on http://path2/resource2 I would like to direct them to a different authentication provider. These url paths are REST based web services calls so that's why they're stateless and not coming from a form. Currently, all authentication providers get executed. What is the best approach for this situation? I'm using spring-security 3.1.0.M1.
<http pattern="/path1/**" create-session="stateless">
<intercept-url pattern="/**" access="ROLE_USER,ROLE_VAR,ROLE_ADMIN" />
<http-basic />
</http>
<http pattern="/path2/**" create-session="stateless">
<intercept-url pattern="/**" access="ROLE_USER,ROLE_VAR,ROLE_ADMIN" />
<http-basic />
</http>
You can define an authentication-manager reference in each http block:
<http pattern="/api/**" authentication-manager-ref="apiAccess">
...
</http>
<http auto-config = "true" authentication-manager-ref="webAccess">
...
</http>
<!-- Web authentication manager -->
<authentication-manager id="webAccess">
<authentication-provider
user-service-ref="userService">
</authentication-provider>
</authentication-manager>
<!-- API authentication manager -->
<authentication-manager id="apiAccess">
<authentication-provider
user-service-ref="developerService">
</authentication-provider>
</authentication-manager>
This feature has been added in Spring Security 3.1.
This works for me:
<security:authentication-manager alias="basicAuthenticationManager">
<security:authentication-provider user-service-ref="accountService">
<security:password-encoder hash="sha"/>
</security:authentication-provider>
<security:authentication-provider user-service-ref="accountService"/>
</security:authentication-manager>
<bean id="basicProcessingFilter" class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager">
<ref bean="basicAuthenticationManager" />
</property>
<property name="authenticationEntryPoint">
<ref bean="basicProcessingEntryPoint" />
</property>
</bean>
<bean id="basicProcessingEntryPoint"
class="com.yourpackage.web.util.CustomBasicAuthenticationEntryPoint">
<property name="realmName" value="yourRealm" />
</bean>
<!-- Stateless RESTful service using Basic authentication -->
<security:http pattern="/rest/**" create-session="stateless" entry-point-ref="basicProcessingEntryPoint">
<security:custom-filter ref="basicProcessingFilter" position="BASIC_AUTH_FILTER" />
<security:intercept-url pattern="/rest/new" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:intercept-url pattern="/rest/**" access="ROLE_USER" />
</security:http>
<!-- Additional filter chain for normal users, matching all other requests -->
<security:http use-expressions="true">
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<security:form-login login-page="/signin"
authentication-failure-url="/signin?signin_error=1"
default-target-url="/"
always-use-default-target="true"/>
<security:logout />
</security:http>
I implemented the authentication entry point because I needed to send some special error codes in certain situations but you don't need to do so.