I've been trying out 'Method Level' security on this application I've been working on. The idea is to secure a method which gets called from the presentation layer using DWR.
Now, I've tried adding the following annotations on my method:
#PreAuthorize("isAuthenticated() and hasRole('ROLE_CUSTOMER')")
And corresponding entry in my security context:
<global-method-security pre-post-annotations="enabled" />
On similar lines, I've tried #Secured annotation:
#Secured({"ROLE_CUSTOMER" })
And corresponding entry in my security context:
<global-method-security secured-annotations="enabled" />
Ideally, I would expect that if a user is not authenticated, they should be redirected to a 'Sign in' page and the 'ROLES' should not be checked. In this case, even for an unauthenticated user, this method call results in 'AccessDeniedException'. I need it to redirect the user to a login page in such a scenario.
To take it forward, I even tried handling the accessdenied exception by creating a custom AccessDenied Handler. Unfortunately, the handler never got called but the exception was thrown.
Here's the configuration:
<access-denied-handler ref="customAccessDeniedHandler"/>
This has a corresponding handler bean defined in the same file.
Still no luck. The accessdeniedhandler never gets called.
Just to summarize the requirement, I need to secure a method. If this method gets called, and the user is unauthenticated the user should get redirected to 'Sign In' page (which as of now is throwing Accessdenied execption).
Appreciate your help folks..
EDIT 1: Here is a snippet from the security context:
<http>
<intercept-url pattern="/*sign-in.do*" requires-channel="$secure.channel}" />
.....
.....
.....
<intercept-url pattern="/j_acegi_security_check.do" requires-channel="${secure.channel}" />
<intercept-url pattern="/*.do" requires-channel="http" />
<intercept-url pattern="/*.do\?*" requires-channel="http" />
<form-login login-page="/sign-in.do" authentication-failure-url="/sign-in.do?login_failed=1"
authentication-success-handler-ref="authenticationSuccessHandler" login-processing-url="/j_acegi_security_check.do"/>
<logout logout-url="/sign-out.do" logout-success-url="/index.do" />
<session-management session-authentication-strategy-ref="sessionAuthenticationStrategy" />
<access-denied-handler ref="customAccessDeniedHandler"/>
</http>
<beans:bean id="customAccessDeniedHandler" class="com.mypackage.interceptor.AccessDeniedHandlerApp"/>
I'm not very familiar with DWR, but I know that it's an RPC mechanism. The problem is that an RPC request sent by the client side javascript is not a regular page request initiated by the user who navigates the browser. The response to such an RPC request cannot possibly make the browser navigate away from the current page, because the response is processed by your javascript code and not by the browser.
What you can do is:
Implement a sort of eager authentication: redirect the user to a simple .jsp login page before allowing it to access the webapp (URL) that makes DWR (or any kind of RPC) requests.
If the AccessDeniedException thrown by the remote procedure gets propagated to the client side in some form, you can try to handle it from javascript code, for example by popping up a login dialog, and submitting user's credentials in an AJAX request. (In this case make sure to save the returned session id, and send it back with each subsequent requests.)
I would be interested in seeing the rest of your security.xml file (specifically, the <http> element), because if every user is being denied, it suggests to me that you have an <intercept-url> property which overrides calls to your controller class(es). If not, the added information might help shed some light on the problem.
Assuming I'm way off so far, another likely error point might be the specified access-denied-page itself -- if its path is a protected path (i.e. by <intercept-url>), or if it isn't specified, you might see the errors of which you speak.
Of course, I, too, am unfamiliar with DWR, so I'm somewhat obviously assuming a standard Spring MVC framework...
Okay, I have not had much success finding why I got an AccessDeniedException. Regardless, I've worked my way out of it till the time I find the reason. Here's a couple of approaches I took:
1) As mentioned by Zagyi, I was able to propagate the AccessDenied Exception over to the client side. I could create an exception handler to and redirect the user to the sign in page.
However, I took a different approach (which may not be the most optimal but seems to work as of now. Here's what I've done:
1) Created a DWRAjaxFilter and mapped to only the Remote Objects I am interested in. This would bean only the DWR calls to these remote methods get intercepted by the filter. This is because I do not want all DWR exposed methods to as for a sign in.
<create creator="spring" javascript="downloadLinksAjaxService">
<param name="beanName" value="downloadLinksAjaxService" />
<include method="methodOne" />
<include method="methodTwo" />
<filter class="com.xyz.abc.interceptor.DwrAjaxFilter"></filter>
</create>
2) Here is the actual filter implementation:
public class DwrSessionFilter implements AjaxFilter {
public Object doFilter(final Object obj, final Method method,
final Object[] params, final AjaxFilterChain chain)
throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
if (!auth.isAuthenticated()
|| auth.getAuthorities().contains(
new GrantedAuthorityImpl("ROLE_ANONYMOUS"))) {
throw new LoginRequiredException("Login Required");
} else {
return chain.doFilter(obj, method, params);
}
}
}
3) Here is the client side handler:
function errorHandler(message, exception){
if(exception && exception.javaClassName == "org.directwebremoting.extend.LoginRequiredException") {
document.location.reload();
}
}
4) I also added and exception mapper for DWR so that I could convert JAVA exceptions to JS exceptions:
<convert match="java.lang.Exception" converter="exception">
<param name='include' value='message'/>
</convert>
5) This seems to work well as of now. I'm still testing this to see if this fails somewhere.
Would appreciate any more inputs.
Related
When Spring Security evolved from version 3.2.x to 4.0.0, multiple modifications became required when using XML based configuration, as stated in the Migration reference. After following throught, I'm still struggling with a few issues regarding the new form-login#login-processing-url attribute. Even though I have specified it to continue using /j_spring_security_check as path, it keeps the new /login path active.
I have been using Spring Security along with SpringMVC and began developing with version 3.2.7. I have a Controller mapping the path /login to a page that shows the login form and depending on possible parameters received shows a certain error message. Below is my controller
#Controller
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout,
Model model) {
if (error != null) {
model.addAttribute("error", "Usuário e senha inválidos.");
}
if (logout != null) {
model.addAttribute("msg", "Logout bem sucedido.");
}
return "login";
}
}
In version 3.2, my spring-security.xml file specified the form-login#login-page parameter to /login, therefore showing my personalized login form (the same path was used for <logout>).
Now, even though I have explicitily configured form-login#login-processing-url, the /login still redirects to spring security's default login page. Besides it, even though I have also specified a logout#logout-url attribute, when login out spring still redirects me to the default login-form with the "logout successful" message, instead of my own controller to show my personalized logout message.
When I use the /login? path, the application shows my own login form, but if I call /login?logout or /login?error, which would, respectively, show my logout and error messages, Spring redirects to the default login page.
Question: Is there a way to fully disable the /login redirection?
I know I could simply change my personalized path to something else instead, such as /signin and avoid the confusion, but I'm doing it as an exercice to understand more about Spring Security 4.x. Besides, I'm concerned an user typing the URL instead of navigating through links would simply drop on spring's default login page.
Additionally, follows an excerpt of my spring-security.xml file, already adapted to version 4.x.
<http auto-config="true" use-expressions="true" disable-url-rewriting="true">
<access-denied-handler error-page="/403" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/power/**" access="hasRole('ROLE_POWER_USER')" />
<form-login
login-page="/login" <!-- my controller -->
default-target-url="/"
login-processing-url="/j_spring_security_check" <!-- to reproduce 3.2 behaviour -->
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout"
logout-url="/j_spring_security_logout"/>
<headers disabled="true"/>
<csrf disabled="false"/> <!-- I know it could be ommited -->
</http>
UPDATE (04/16/2015): Looks like a bug on version 4.0.0. Check my answer.
What you need to do is implement a custom AuthenticationEntryPoint to turn off login redirect (which is set to default).
Here is some starter reading information:
http://docs.spring.io/spring-security/site/docs/4.0.0.RELEASE/reference/htmlsingle/#auth-entry-point
Here is also a example of how it is implemented:
http://krams915.blogspot.com.au/2010/12/spring-security-mvc-integration-using_26.html
I have changed my login controller mapping to /access. My login not only works (as expected), but for some reason the application stopped returning Spring's default login form when accessing /login.
It simply does not recognize the path (404 error). The behaviour I described previously, confusion between default and custom login forms when using /login looks to me like a bug. Let's hope gets fixed in future versions.
Therefore, the "fix" I suggest is changing the controller mapping, even though this my reflect in changing the calls on multiple views.
I am having problems with redirecting to access token entry point /oauth/token which will detail bellow. I am hoping someone could give me some light to it as I took a lot of time
implementing this.
Also, interesting is the fact that I cannot test with with SoapUI 5.0 community edition even following their instructions. It gest the authorization code but fails later as you need to set the redirect URI as "urn:ietf:wg:oauth:2.0:oob".
Since Spring-Security-Oauth2 lacks a lot of good documentation and I had spent lots of time debugging and documenting the work I decided to share
my comments and configuration code here which might also be helpfull to someone else.
I am using the following dependencies versions on my pom:
<org.springframework-version>4.0.5.RELEASE</org.springframework-version>
<org.springframework.security-version>3.2.5.RELEASE</org.springframework.security-version>
<org.springframework.security.oauth2-version>2.0.3.RELEASE</org.springframework.security.oauth2-version>
Now, the idea was to implement all the clientId objects, UserPrincipal, access, nonce and token stores using Cassandra as a persistency store. All those components
are working perfectly and are tested. In fact, it fetches all the authentication/authorization, creates authorization codes.
I've seen a bug recently on testing JDBC stores on Spring Oauth2 github but that was related to testing not the actuall implementation, specially because not
using JDBC.
I have wrote a client webapplication to access a REST resource which resides with the OAuth2 servers and Spring Security for logging in. All goes well until I go request
an access token to /oauth/token.
When I hit the secure Rest service first it properly starting doing redirections and goes tru DefaultOAuth2RequestFactory createAuthorizationRequest() method. Loads
the ClientDetails object perfectly with the secret etc from the store. So it has all the scopes, grants etc for the Oauth2 client. It also validates properly the redirectURIParameter
and resolves the redirect. Then it goes to the TokenStoreUserApprovalHandler and create the OAuth2Request object. Then, of course, tries to find an existing access token which
does not exist yet on the workflow. It creates the authenticationkey from authenticationKeyGenerator and queries the store which properly returns null at this point.
Then it redirects back to /oauth/authorize twice when on the second time it has an authorization code and marks as approved (AuthorizationEndPoint) inside approveOrDeny() method.
The authorizationCodeServices creates the code and stores it properly in my Cassandra store.
At this point it calls (AuthorizationEndPoint) getSuccessfulRedirect() where it adds the state parameter to the template.
Then it calls (OAuth2RestTemplate class) getAccessToken() method. Since the access token is fine and valid it then calls acquireAccessToken() which returns an accessTokenRequest with
{code=[Q19Y6u], state=[1PyzHf]} . Then it calls the accessTokenProvider to obtain an access token at obtainAccessToken(). Then the calls OAuth2AccessTokenSupport is called at
retrieveToken() method. And fails at getRestTemplate. The AuthorizationCodeResourceDetails is created perfectly with grant type authorization_code and it has both authorizationScheme
and clientAuthenticationScheme as header. The clientId is correct as the clientSecret. The id of the AuthorizationCodeResourceDetails is oAuth2ClientBean and the userAuthorizationURI is
http://myhost.com:8080/MyAPI/oauth/authorize. Headers show as
{Authorization=[Basic .....]}
The extractor is org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.
The form is {grant_type=[authorization_code], code=[Xc7yni], redirect_uri=[http://myhost.com:8080/OAuthClient/support]}
And then the application freezes and it shows on the logs:
DEBUG: org.springframework.security.authentication.DefaultAuthenticationEventPublisher - No event was found for the exception org.springframework.security.authentication.InternalAuthenticationServiceException
DEBUG: org.springframework.security.web.authentication.www.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.InternalAuthenticationServiceException
Then I have the following Exception on my client web application:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is error="access_denied", error_description="Error requesting access token."
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
Now, I believe I have a few things wrong on my xml configuration for OAuth2 and Spring Security. So the configuration file follows.
I do have a few questions concerning it so here are the questions first:
1) <oauth2:authorization-server> I am not sure if I had this configured correctly. Please look at the comments I have on my xml file.
I have added the authorization-request-manager-ref parameter which points to my userAuthenticationManager bean, a authentication manager
which takes a authentication-provider user-service-ref="userService"
<oauth2:authorization-server client-details-service-ref="webServiceClientService"
token-services-ref="tokenServices" user-approval-page="/oauth/userapproval"
error-page="/oauth/error" authorization-endpoint-url="/oauth/authorize"
token-endpoint-url="/oauth/token" user-approval-handler-ref="userApprovalHandler"
redirect-resolver-ref="resolver">
<oauth2:authorization-code
authorization-code-services-ref="codes" />
<oauth2:implicit/>
<oauth2:refresh-token/>
<oauth2:client-credentials/>
<oauth2:password authentication-manager-ref="userAuthenticationManager"/>
<!-- <oauth2:custom-grant token-granter-ref=""/> -->
</oauth2:authorization-server>
2) authentication-manager oauthClientAuthenticationManager is used when "/oauth/token" is intercepted.
This is defined as follows:
<sec:authentication-manager id="oauthClientAuthenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService">
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
3) I have the following methodSecurityExpressionHandler bean defined which is used at sec:global-method-security.
Not sure if this is correct or not as well.
<beans:bean id="methodSecurityExpressionHandler"
class="org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler" />
4) I also have a bean "clientCredentialsTokenEndpointFilter" which I believe is not recommended.
I use this as a custom-filter for entry point "/oauth/token" but I believe this is wrong.
<beans:bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<beans:property name="authenticationManager" ref="oauthClientAuthenticationManager"/>
</beans:bean>
A filter and authentication endpoint for the OAuth2 Token Endpoint. Allows clients to authenticate using request
parameters if included as a security filter, as permitted by the specification (but not recommended). It is
recommended by the specification that you permit HTTP basic authentication for clients, and not use this filter at
all.
5) Now for the Oauth Token Endpoint:
This is the endpoint /oauth/token as I have many questions here:
This is never reached.
Shall I have a custom-filter like clientCredentialsTokenEndpointFilter to it or not?
Do I have to have a http-basic entry point?
Shall I have an access attribute as in IS_AUTHENTICATED_FULLY or can I use an authority I have defined on my UserPrincipal object such as OAUTH_CLIENT I have added there?
What about the session? Shall I say "stateless" or
"never"
Shall I add the corsFilter to it as well?
Is the entry point correct? Which is the OAuth2AuthenticationEntryPoint class?
Do I have to add the csrf token? I believe not as it will
restrict it more.
Is the expression-handler correct as a org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandle?
The authentication-manager-ref I can change from oauthClientAuthenticationManager to userAuthenticationManager.
<sec:http use-expressions="true" create-session="stateless"
authentication-manager-ref="userAuthenticationManager"
entry-point-ref="oauthAuthenticationEntryPoint" pattern="/oauth/token">
<sec:intercept-url pattern="/oauth/token" access="hasAuthority('OAUTH_CLIENT')" />
<!-- <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> -->
<sec:http-basic entry-point-ref="oauthAuthenticationEntryPoint"/>
<!-- <sec:http-basic/> -->
<sec:anonymous enabled="false" />
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
<sec:expression-handler ref="webSecurityExpressionHandler" />
<!-- <sec:custom-filter ref="corsFilter" after="LAST"/> -->
</sec:http>
I would like to add the full config file here but there is a limit.
The /oauth/token endpoint should be secured with client credentials, and it looks like you wired it to a user authentication manager. You didn't show the configuration or implementation of that, but judging by the InternalAuthenticationServiceException it is failing with an exception that isn't classified as a security exception. Fix those two things and you might be in business.
(The #Configuration style is much more convenient by the way, and I would recommend getting started with that and more of the defaults it provides, until you get the hang of it.)
I am having an issue that is very strange to me so maybe someone can help.
Inside my security.xml I have this:
<http auto-config="true">
<intercept-url pattern="/app/admin/**" access="ROLE_ADMIN"/>
<intercept-url pattern="/app/**" access="ROLE_ADMIN,ROLE_USER"/>
<form-login login-page="/login" authentication-failure-url="/login?error=true" login-processing-url="/j_security_check" authentication-success-handler-ref="authSuccessHandler"/>
<remember-me user-service-ref="userDao" key="e37f4b31-0c45-11dd-bd0b-0800200c9a66" authentication-success-handler-ref="authSuccessHandler"/>
</http>
Everything was working fine until I added: authentication-success-handler-ref="authSuccessHandler", which is simple and looks like this:
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
logger.info("authenticated");
User user = userManager.getUserByUsername(request.getRemoteUser());
Hibernate.initialize(user.getMessagesList());
request.getSession(true).setAttribute("userMessages", user.getMessagesList());
}
Now that all works completely fine, the issue now is that on every login or rememberMe login the first page accessed is ALWAYS blank no matter the url. And on a normal login the page that comes up blank is /j_security_check so it seems like something is not getting processed because of my success handler, as expected when removing the handler everything works fine. Am I missing something here? please help me out.
Normally when you don't explicitly configure an AuthenticationSuccessHandler spring configures a SavedRequestAwareAuthenticationSuccessHandler for you for handling the success case. What it does is after successful authentication sends the user to the initially requested URL (or when specified and forced to the default page specified in the configuration).
Your custom implementation does no such thing it only sets something on the session and is done, it doesn't send anything back to the client. What you probably want is to extend the SavedRequestAwareAuthenticationSuccessHandler do your thing and then call super.onAuthenticationSuccess
I was reading many tutorials and none of them is working for me...
I use Spring 3.1.x with Spring Security. I have a bunch of secured url and many unsecured. Now, when the user is logged in and tries to logout I want him to stay in the same page as he was before so I use this:
<beans:bean id="logoutSuccessHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler">
<beans:property name="useReferer" value="true"/>
</beans:bean>
This works fine, however when the user logs out from the secured page it redirects him to the login page, and I would like to redirect to home page.. How can I achieve this?
Thanks in advance!
Since you have custom logic for redirecting, you need a custom LogoutSuccessHandler.
In this handler, you need to add this logic:
String refererUrl = request.getHeader("Referer");
String normalizedRefererUrl = ...; // "Normalize" (if needed) the URL so it is in the form that you need.
if (requiresAuthentication(normalizedRefererUrl, authentication)) {
response.sendRedirect(request.getContextPath()); // home page
} else {
response.sendRedirect(refererUrl); // or normalizedUrl
}
In your requiresAuthentication() method, you need to use some part of Spring Security that determined if the URL needs authentication.
You can use a WebInvocationPrivilegeEvaluator reference there. You get a hold of it through Spring through autowiring by class (since there will be a bean implementing WebInvocationPrivilegeEvaluator).
The evaluator has a method that you can use, isAllowed(uri, authentication).
<security:http auto-config="true">
<security:form-login login-page="/spring/login"
login-processing-url="/spring/loginProcess"
default-target-url="/spring/main"
authentication-failure-url="/spring/login?login_error=1" />
<security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" />
</security:http>
logout-success-url from the docs or for a custom succeshandler
as per documentation for spring security if you don't specify logout-success-url then it should be redirecting /. check this configuration and may be you can use SimpleRedirectInvalidSessionStrategy
We are using Spring Security for managing authentication. The issue we are seeing is that when a user's session is timed out between bringing up a GET form and hitting the save button that does a POST, they are sent to the login page but spring is saving the original post information in the session.
Our app does not bring them back to the original URL after login, but instead sends them back to a common starting page. This works fine, but when the user happens to return to the page they had originally tried to POST to (the form GET and POST are the same URLs) Spring tries to resubmit the POST automatically which is not what we want.
Is there a way to completely disable the SavedRequest storing logic in Spring?
I guess this jira issue of spring security describes your problem and how to handle this.
Based on Nathan's comment on Raghuram's answer, with namespaced XML it's something like this:
<security:http>
<security:request-cache ref="nullRequestCache" />
<!-- ... -->
</security:http>
<bean id="nullRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache" />
There are two scenarios:
1) If you want that after relogin, user should always get forwarded to the default target URL instead of the orginal requested URL then put always-use-default-target="true" in your security.xml like
<http auto-config="true">
.....
<form-login login-page="/login" always-use-default-target="true" default-target-url="/xyz"
authentication-failure-url="/login?error=true" login-processing-url="/j_security_check"/>
</http>
1) If you want that on session timeout after relogin, user should forward to the orginal requested URL but you do not want to resubmit the form then put session-fixation-protection="newSession" in your security.xml like
<http auto-config="true">
<session-management session-fixation-protection="newSession"/>
.....
</http>
Please put session-management tag as first line in http configuration.
It looks like the session-fixation-protection="newSession" attribute on (2.0) or (3.0) will also resolve the issue
With Spring 4.2.5 I ran into this too.
My case was almost identical: display GET form, wait for session timeout, then POST the form. In my app after re-authentication a start page is displayed. However, if the user then navigates to this GET form, and POSTs it, then the previous POST parameters are remembered and concatenated to the current request, resulting in comma separated values in the #RequestParam variables.
I dumped the session in my authentication controller and indeed I saw a "SPRING_SECURITY_SAVED_REQUEST" named key.
The spring documentation says that by default a "SavedRequestAwareAuthenticationSuccessHandler" is used for retrieving the saved request data from the session and apply it to the request.
I tried to use a do-nothing successHandler but couldn't make it work.
I also tried applying
http.sessionManagement().sessionFixation().newSession();
to the security config but that didn't help.
However
http.requestCache().requestCache(new NullRequestCache());
solved the issue.