How can I allow a user to override using Spring Security?

My Spring MVC web application has certain areas that are accessible only to users with sufficient privileges. Instead of just having a "denied access" message, I should be able to allow users to log in as another user to use these pages (sort of like an override).

How to do it with Spring Security?

Here's the thread I want to have, a little more detail:

  • User A enters page X from an external application and authenticates through the headers.
  • User A does not have permission to use page X, and therefore appears on the login screen with a message that they must be logged in as a user with sufficient privileges to use this page.
  • User B logs in and has sufficient privileges and is transferred to page X.

Note. Page X contains a large long query string that must be saved.

How to do it with Spring Security?


Here is my Spring Security Configuration File:

<?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.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <debug /> <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> <!-- Allow anyone to get the static resources and the login page by not applying the security filter chain --> <http pattern="/resources/**" security="none" /> <http pattern="/css/**" security="none" /> <http pattern="/img/**" security="none" /> <http pattern="/js/**" security="none" /> <!-- Lock everything down --> <http auto-config="true" use-expressions="true" disable-url-rewriting="true"> <!-- Define the URL access rules --> <intercept-url pattern="/login" access="permitAll" /> <intercept-url pattern="/about/**" access="permitAll and !hasRole('blocked')" /> <intercept-url pattern="/users/**" access="hasRole('user')" /> <intercept-url pattern="/reviews/new**" access="hasRole('reviewer')" /> <intercept-url pattern="/**" access="hasRole('user')" /> <form-login login-page="/login" /> <logout logout-url="/logout" /> <access-denied-handler error-page="/login?reason=accessDenied"/> <!-- Limit the number of sessions a user can have to only 1 --> <session-management> <concurrency-control max-sessions="1" /> </session-management> </http> <authentication-manager> <authentication-provider ref="adAuthenticationProvider" /> <authentication-provider> <user-service> <user name="superadmin" password="superadminpassword" authorities="user" /> </user-service> </authentication-provider> </authentication-manager> <beans:bean id="adAuthenticationProvider" class="[REDACTED Package].NestedGroupActiveDirectoryLdapAuthenticationProvider"> <beans:constructor-arg value="[REDACTED FQDN]" /> <beans:constructor-arg value="[REDACTED LDAP URL]" /> <beans:property name="convertSubErrorCodesToExceptions" value="true" /> <beans:property name="[REDACTED Group Sub-Tree DN]" /> <beans:property name="userDetailsContextMapper" ref="peerReviewLdapUserDetailsMapper" /> </beans:bean> <beans:bean id="peerReviewLdapUserDetailsMapper" class="[REDACTED Package].PeerReviewLdapUserDetailsMapper"> <beans:constructor-arg ref="UserDAO" /> </beans:bean> </beans:beans> 

I am using a slightly modified version of the Spring Security 3.1 Active Directory connection capabilities. Modifications simply load all user groups, including those that are achieved by grouping groups, and not just those to which the user directly logs. I also use a user user object in which my User Object application is embedded, and a custom LDAP mapper that performs normal LDAP mapping and then adds my user.

There is a special authentication scenario that has not yet been implemented when a user is authenticated based on the user name transmitted from an external application (or through Kerberos) in Single Sign-On mode.

+6
source share
2 answers

How do you test roles?

If you define them in your security context as follows:

 <intercept-url pattern="/adminStuff.html**" access="hasRole('ROLE_ADMIN')" /> 

You can set defaultFailureUrl to SimpleUrlAuthenticationFailureHandler , and when a less privileged user tries to access the secure URL, FaliureHandler should redirect you to defaultFailureUrl , which may be your login page.

You can enter FaliureHandler in filter at position FORM_LOGIN_FILTER .

 <bean id="myFaliureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <property name="defaultFailureUrl" value="http://yourdomain.com/your-login.html"/> </bean> <bean id="myFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <property name="authenticationFailureHandler" ref="myFaliureHandler"/> </bean> <http> <custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" /> </http> 

Answer 1) in the comment.

This will be a little more than I thought, given your namespace configuration.

What you need to do is remove the <form-login> definition and add "custom" UsernamePasswordAuthenticationFilter instead (this is the filter that processes the <form-login> element).

You also need to remove <access-denied-handler> .

Thus, your configuration will look something like this:

 <bean id="myFaliureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> <property name="defaultFailureUrl" value="http://yourdomain.com/your-login.html"/> </bean> <bean id="myFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <property name="authenticationFailureHandler" ref="myFaliureHandler"/> <!-- there are more required properties, but you can read about them in the docs --> </bean> <bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> <property name="loginFormUrl" value="/login"/> </bean> <http entry-point-ref="authenticationEntryPoint" auto-config="false"> <!-- your other http config goes here, just omit the form-login element and the access denied handler --> <custom-filter position="FORM_LOGIN_FILTER" ref="myFilter" /> </http> 

As a rule, also see spring docs on custom filters , if you have not already done so. We are currently using this config in my current company, forcing users to switch if privileges are not required on the page.

+2
source

Solution 1

Register your entire ExeptionResolver app using whatever you like. For instance,

 public class MyApplicationErrorResolver extends SimpleMappingExceptionResolver { @Autowired private List<LogoutHandler> logoutHandlers; @Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if(ex instanceof AccessDeniedException) { for(LogoutHandler lh : logoutHandlers) { lh.logout(request, response, SecurityContextHolder.getContext().getAuthentication()); } // Not present as a bean. So create it manually. SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); logoutHandler.setInvalidateHttpSession(true); logoutHandler.logout(request, response, SecurityContextHolder.getContext().getAuthentication()); return new ModelAndView(new RedirectView(request.getRequestURL().toString())); } return super.doResolveException(request, response, handler, ex); } } 

register it as a bean:

 <bean class="package.path.MyApplicationErrorResolver" /> 

(this is all you need to register). This will work for your configuration. You will probably need to remove the <access-denied-handler> element from the configuration.

Decision 2

Another way is to use AccessDeniedHandler . For example:

 public class MyAccessDeniedExceptionHandler implements AccessDeniedHandler { @Autowired private List<LogoutHandler> logoutHandlers; @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { for(LogoutHandler lh : logoutHandlers) { lh.logout(request, response, SecurityContextHolder.getContext().getAuthentication()); } SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); logoutHandler.setInvalidateHttpSession(true); logoutHandler.logout(request, response, SecurityContextHolder.getContext().getAuthentication()); response.sendRedirect(request.getRequestURL().toString()); } } 

register it as a bean:

 <bean id="accesssDeniedHandler" class="package.path.MyAccessDeniedExceptionHandler" /> 

and specify it in your configuration:

 <access-denied-handler ref="accesssDeniedHandler" /> 
0
source

Source: https://habr.com/ru/post/898094/


All Articles