I've run into a such situation: my application has several roles(administrator, moderator, user). Moderator and User can edit some forms. All permisions are ok. But when I'm loggen in as a user(role User) and change an id in the url, I can simply get and edit form of another user(role User).
How to deny access and prevent such actions?
ps. version of spring and spring security is 3.1.2
update - added spring security context
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http use-expressions="true" auto-config="false"
entry-point-ref="authenticationEntryPoint" access-denied-page="/403.jsp">
<form-login login-page="/login.html"
authentication-failure-url="/login.html?error=true"
login-processing-url="/j_spring_security_check"
authentication-success-handler-ref="successHandler" />
<logout logout-url="/logout" logout-success-url="/login.html" />
<intercept-url pattern="/admin/**" access="hasRole('adminViewPermission')" />
<intercept-url pattern="/moderator/**" access="hasRole('moderatorViewPermission')" />
<intercept-url pattern="**/form-management"
access="hasRole('formManagementPermission')" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService" />
</authentication-manager>
<beans:bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login.html" />
<beans:property name="forceHttps" value="false" />
</beans:bean>
<beans:bean id="successHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/authenticate" />
<beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
</beans:bean>
<beans:bean id="userDetailsService"
class="com.jack.MyYserDetailsService">
</beans:bean>
</beans:beans>
It looks like you want take into account actual domain object for your security rule. Normal SpringSecurity setup with users and roles can add security rules like this: who (athenticated user with some role) may access to some URL / method invocation. If you want to be able use enhanced rules like this: who (athenticated user with some role) may access to some URL / method invocation and what domain objects he can use then you need to use ACL feature.
EDIT. But if you need just one security rule like this then set up ACL may be an overkill. You can try enhance your actual SpringSecurity setup by custom web security expression:
<intercept-url pattern="/moderator/**" access="hasRole('moderatorViewPermission') and userIsAuthor()" />
Where your userIsAuthor() method will:
extract id of the object from the URL (I suppose something like /moderator/item/56)
check if current user is an author of an item id = 56.
Related
I have a java spring mvc web application and I have implemented the login part using spring security. The version of spring security that I use is 3.2.5. My spring-security.xml file is as follows:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.2.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="isAnonymous()" />
<form-login login-page="/login" default-target-url="/welcome"
authentication-failure-url="/login?error" username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout" invalidate-session="false" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
</http>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
<beans:bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref local="daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<!-- Declare an authentication-manager to use a custom userDetailsService -->
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="bcrypt" />
</authentication-provider>
</authentication-manager>
</beans:beans>
Now I have a requirement where I will display some content initially and then the user has to login to view the full content. So I provide a sign in link which will bring up the sign in form. But when a user logs in, the default page is shown. I want the user to be redirected to the URL where the user is already in. I have also tried to POST the form to one of my custom controller methods and then redirect to spring security check from there. I intend to store the current URL to session from the method and redirect the user to that method from the default target method of spring security. But redirecting to spring security check is always giving me invalid username or password error since it is not able to authenticate the user. My custom method would be as follows:
#RequestMapping(value = "/edu-login", method = RequestMethod.POST)
public String eduLogin(#ModelAttribute ("username") String username, #ModelAttribute ("password") String password, Model model, HttpServletRequest request, HttpServletResponse response, RedirectAttributes ra)
{
//My custom logic to stroe url to session
ra.addAttribute("username", username);
ra.addAttribute("password", password);
return "redirect:/j_spring_security_check";
}
Is there any solution for my problem. All I want is the user to return to the url on the browser after login.
There is a solution for your use case, but that's not the correct flow you should apply. More over, you cannot use RedirectAttributes this way, as it only applies in MVC layer and the form-loginfilter is in filter layer.
The sequence you are trying to achieve is this:
public_url > [login:if required] > private_url
For such a flow, there is a component you could take advantage of, which is org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler. As it states in the javadoc:
An authentication success strategy which can make use of the DefaultSavedRequest which may have been stored in the session by the ExceptionTranslationFilter. When such a request is intercepted and requires authentication, the request data is stored to record the original destination before the authentication process commenced, and to allow the request to be reconstructed when a redirect to the same URL occurs. This class is responsible for performing the redirect to the original URL if appropriate.
So, let's say your existing unprotected url is /public/edu and the second url (the protected one) is /private/edu.
In /public/edu you should provide a link to /private/edu. While accesing /private/edu the SpringSecurityFilterChain would check if the user is authenticated and have the required authorizations.
If the user is already authenticated the url will be reached directly.
If not, it will redirected to login while it keeps the requested url /private/edu temporaly in session. Once the user performs a correct login, the SavedRequestAwareAuthenticationSuccessHandler will redirect the user to /private/edu instead of the default success url page.
This could be a sample config:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.2.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="isAnonymous()" />
<intercept-url pattern="/private/edu" access="isAnonymous()" />
<form-login
login-page="/login"
authentication-success-handler-ref="savedRequestSuccesHandler"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout" invalidate-session="false" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
</http>
<beans:bean id="savedRequestSuccesHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/welcome" />
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
<beans:bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref local="daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<!-- Declare an authentication-manager to use a custom userDetailsService -->
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="bcrypt" />
</authentication-provider>
</authentication-manager>
</beans:beans>
Note I've removed the default-target-url="/welcome" of the <http> element and introduced the property authentication-success-handler-ref="savedRequestSuccesHandler" which referers to a new bean I've just created:
<beans:bean id="savedRequestSuccesHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/welcome" />
</beans:bean>
I have two http patterns, It is corresponding with two login forms . One for User login with namespace default "/", one for Admin login with namespace "/admin".
I have a problem when I make interceptors for each login form.
The errors happened when I inputted wrong url at admin's login form. (eg .../admin/sdfsdfa). I doesn't redirect to admin's login form
"Unable to load page,because Too many redirects".
My spring-security.xml:
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.1.xsd">
<http pattern="/login**" security="none" />
<http pattern="/admin/" security="none" />
<http pattern="/admin/**">
<intercept-url pattern="/**" access="ROLE_ADMIN" />
<form-login login-page="/admin/adminLogin"
login-processing-url="/admin/j_spring_security_check"
default-target-url="/admin/adminAccess" authentication-failure-url="/admin/adminLogin?error"
username-parameter="username" password-parameter="password" />
<logout logout-url="/admin/j_spring_security_logout"
logout-success-url="/adminLogin?logout" />
</http>
<http>
<intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />
<access-denied-handler error-page="/user/403.jsp" />
<form-login login-page="/login" default-target-url="/userAccess"
authentication-failure-url="/login?error" username-parameter="username"
password-parameter="password" />
<logout logout-success-url="/login?logout" />
</http>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService" />
</beans:bean>
<beans:bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref local="daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<authentication-manager erase-credentials="false">
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
</authentication-manager>
When I login with user's login form (namespace default "/"). It works well, It can intercept all requests so We can't access any url without login success. It automatically redirect url into url user's login form.
But when I login with admin's login form (namespace default "/admin").
I see spring-security interceptors work wrong or doesn't work. At namespace of admin "/admin". When I input wrong url (eg .../admin/sdfnsdfe). It doesn't redirect into admin's login form
It display error "Unable to load page,because Too many redirects".
I think url have matched all above patterns, So the errors happened.
Now what things I need do to resolve this problem ?
Do you think I should make namespace for user login is "/user" replace namespace default "/" ?
When you input URL like /admin/sdfsdfa, the URL match a pattern of /admin/** which you define it as a secured resource with <intercept-url pattern="/**" access="ROLE_ADMIN" />.
As the user is not authenticated, he is redirected to the login page /admin/adminLogin. After the redirect, the request URL match the pattern of /admin/** and redirect to /admin/adminLogin again. As a result, you get Too many redirects.
Please try to set your login page of /admin/adminLogin with security="none" and add:
<http pattern="/admin/adminLogin" security="none" />
before
<http pattern="/admin/**">
This can disable the Security filter chain for the request path of /admin/adminLogin
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.
I got a spring + hibernate project that uses spring security for authentication and everything works like a charm. i have the spring-security.xml below :
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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.2.xsd">
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/home" access="isAuthenticated()" />
<intercept-url pattern="/home/**" access="isAuthenticated()" />
<form-login
login-page="/"
authentication-failure-url="/?error"
username-parameter="username"
password-parameter="password"
default-target-url="/home" />
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<!-- logout handling -->
<logout invalidate-session="true" logout-success-url="/?logout" delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE" />
<!-- enable csrf protection <csrf /> -->
<remember-me services-ref="rememberMeServices" key="clarkerpi" />
</http>
<beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:property name="tokenRepository" ref="customTokenRepository" />
<beans:property name="userDetailsService" ref="userDetailsService" />
<beans:property name="key" value="clarkerpi" />
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="authenticationProvider" />
</authentication-manager>
<beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService" />
</beans:bean>
</beans:beans>
Almost everything works. I can login and check "remember me" and it creates cookies, persists tokens and all that. If i erase JSESSIONID cookie i can still access protected resources.
But i have a question...
if i access localhost/projectname/ , and "/" being my login page, is there any native way ( spring security ) of redirecting to target-url, which is /home for those with the remember_me cookie? I can access any protected resource with no problem, but i'd like to type localhost/projectname/ and access /home. Of course, let the login page to be available for non-remember-me logins.
Question 2 ) I'm very new to spring security + cookie handling, is ok to delete JSESSIONID and Remember_me cookies like i'm doing in logout ? Or?
thanks in advance,
//fferrandini
I found a solution.
The spring security does it job perfectly. When you have "remember_me" active and access protected resources it does work like a charm and that is all his job. Spring security doenst take care of redirects or "what i want to do with this after auth"...
the solution was quite simple.
I found this method at mkyong :
private boolean isRememberMeAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return false;
}
return RememberMeAuthenticationToken.class.isAssignableFrom(authentication.getClass());
}
Which you can set it in a controller or even better a filter. I set it in my login mapping... "/". If true redirect to /home otherwise let login happen.
hope this help somebody!
spring you 're beautiful !! lol
Change your login page like this "/login" and
<intercept-url pattern="/login" access="permitAll()"/> and map "/" to "/home"
I have an application that uses SwitchUserFilter. The switching and exiting works fine in normal cases, but if we give the exit url when the user has not actually switched, the user is logged out (expectedly), but when he tries to login again, the application tries to redirect him to the last page that he tried to access (i.e the switch user url).. where again, the user is found to have not switched, and so an error is thrown, the user is logged off, and so the cycle continues.
The following is my spring configuration file :
<?xml version="1.0" encoding="UTF-8"?>
<!-- - Sample namespace-based configuration - -->
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<global-method-security pre-post-annotations="enabled">
<!-- AspectJ pointcut expression that locates our "post" method and applies
security that way <protect-pointcut expression="execution(* bigbank.*Service.post*(..))"
access="ROLE_TELLER"/> -->
</global-method-security>
<http use-expressions="true">
<intercept-url pattern="/**" access="isAuthenticated()" />
<custom-filter after="FILTER_SECURITY_INTERCEPTOR" ref="switchUserProcessingFilter" />
<form-login />
<logout invalidate-session="true" logout-url="/do/logout" />
</http>
<!-- Usernames/Passwords are rod/koala dianne/emu scott/wombat peter/opal -->
<authentication-manager>
<authentication-provider>
<user-service id="userService">
<user name="rod" password="koala" authorities="supervisor, teller, user" />
<user name="dianne" password="emu" authorities="teller, user" />
<user name="scott" password="wombat" authorities="user" />
<user name="peter" password="opal" authorities="user" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="switchUserProcessingFilter"
class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter">
<beans:property name="userDetailsService" ref="userService">
</beans:property>
<beans:property name="switchUserUrl" value="/switchUser" />
<beans:property name="exitUserUrl" value="/exitUser" />
<beans:property name="targetUrl" value="/index.jsp" />
</beans:bean>
</beans:beans>
Any suggestions as to how can I handle it ?
A practical case could be when I have switched the user, and the session times out. Not knowing that the session has timed out, the user tries to click on the 'exit user' url, which then redirects him to the login page, and the above cycle continues.
As mentioned in this thread, I implemented a custom login success handler, mimicking SavedRequestAwareAuthenticationSuccessHandler, which is the default login success handler. It
retrieves the url that we tried to access before being redirected to the login page from the request cache (where it was stored by ExceptionTranslationFilter, on seeing that we weren't authenticated) and redirects to that url. I simply configured it to go to the root url when it encountered something like 'exitUser'