#PreAuthorize and intercept-url priority - spring-security

I have
<security:http use-expressions="true">
<security:intercept-url pattern="/**/*" access="hasRole('ROLE_USER')"/>
in the Spring Security context configuration file and
#PreAuthorize("permitAll")
#RequestMapping("/public")
public String aMethod() {
// ...
}
in a controller.
What I want is that all the URLs to require authentication except public. Is this possible?

<intercept-url> in XML takes precedence over annotations. <intercept-url> works at URL level and annotations at method level.
If you are going to use spring security and spring <form-login /> then the approach below would serve you better.
<intercept-url pattern="/public/**"
access="permitAll" />
<intercept-url pattern="/restricted/**"
access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN', 'ROLE_SOME')
#PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SOME')")
#RequestMapping("/restricted/aMethod")
public String aMethod() {
// ...
}
Anything under restricted can be accessed by three different roles. But specific path restricted/aMethod can be accessed by #PreAuthorize("ROLE_ADMIN") and #PreAuthorize("ROLE_SOME") but NOT by #PreAuthorize("ROLE_USER"). By default all three roles can access but when you mark some path with #PreAuthorize("ROLE_ADMIN") then user with ROLE_ADMIN can access that path.
If you think about it, #PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_SOME')") act as narrowed or filtered access from a large set of ROLES to single(or set of roles) ROLE.
As you would notice, none of /restricted paths are accessible by permitAll. Its preferred to have /static/*.css and others under permitAll.
HTH

Related

LDAP authentication without managerDN and manager password

I am writing an application in Java Spring framework to perform Active Directory LDAP authentication.
I am succeeding in connecting to my organization LDAP.
Here is the configuration settings:Spring-security.xml
<!-- This is where we configure Spring-Security -->
<security:http auto-config="true" use-expressions="true"
access-denied-page="/oops">
<security:intercept-url pattern="/auth/*"
access="isAuthenticated()" />
<security:logout invalidate-session="true"
logout-success-url="/" logout-url="/logout" />
</security:http>
<security:authentication-manager>
<security:ldap-authentication-provider
user-search-filter="(&(sAMAccountname={0})(objectCategory=user))"
user-search-base="DC=am, DC=example, DC=com" group-search-filter="(&(sAMAccountname={0})(objectCategory=group))"
group-search-base="DC=am, DC=example, DC=com">
</security:ldap-authentication-provider>
</security:authentication-manager>
<security:ldap-server url="ldaps://myserver.am.example.com:4567"
manager-dn="CN=Johnson \, Mitchell, OU=San Francisco,DC=am,DC=example,DC=com"
manager-password="sdvsdvsvs" />
My question here is that,is there any way to authenticate LDAP without supplying manager-dn and manager-password in security:ldap-server tag.
Please provide a solution to this.Thanks in advance.
Yes it is possible: you can let the user who is actualy logging in connecting to the LDAP himself to test his credential and fetch its userdata.
AuthenticationManager configuration:
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider = new ActiveDirectoryLdapAuthenticationProvider(domain, url, rootDn);
activeDirectoryLdapAuthenticationProvider.setSearchFilter(searchFilter);
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider);
}
Spring security does two things:
Let the user log in with his username and password
Find the user to fetch user info, groups, etc. For this step, you must specify a searchFilter that can find a user based on it's username, like "userPrincipalName={0}" where {0} is the provided username.
Define an administrative user who has the necessary permissions, and use that. You certainly shouldn't use the managerDN for anything in your application.

update spring security to match url suffix

my spring mvc application uses the following url
http://xyz.com/<war name>/springmvc/login
I have updated my web.xml
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/springmvc/*</url-pattern>
</filter-mapping>
I updated spring security xml file as follows
<http access-decision-manager-ref="accessDecisionManager" auto-config="true">
<intercept-url pattern="/springmvc/welcome*" access="ADMIN" />
<form-login login-page="/springmvc/login" default-target-url="/springmvc/welcome"
authentication-failure-url="/springmvc/loginfailed" />
<logout logout-success-url="/springmvc/logout" />
<remember-me data-source-ref="dataSource"/>
</http>
If i type springmvc/login or springmvc/welcome it goes to login page. but when I enter the username and password, I get a 404. The url changes to http://xyz.com/UserInterface/springmvc/j_spring_security_check. I expect to see hello.jsp as per the controller below
my login controller is as follows
#RequestMapping(value="/welcome", method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername();
model.addAttribute("username", name);
model.addAttribute("message", "Spring Security login + database example");
//logical view name
return "hello";
}
The default login-processing-url property value for form-login is /j_spring_security_check.
i.e. the login form will be posted to this url.
If you context root is UserInterface the default url would be http://xyz.com/UserInterface/j_spring_security_check, but you are creating the url as http://xyz.com/UserInterface/springmvc/j_spring_security_check which spring-security is not able to understand.
You have two options
Change you springSecurityFilterChain url pattern to /* from
/springmvc/*, and make sure that your custom-login form has action
as //j_spring_security_check
Add custom login-processing-url path in form-login

Configuring spring security to redirect user to previous location after session timeout

I have a slight issue with my configuration of spring security and the InvalidSessionStrategy implementation I use.
Say a user is connected to the app and is viewing page: /userArea/thePage and their session times out, the user will first be redirected to the /signin page. Then, upon successful signin, they will be redirected to the home page of they personal area (/userArea) whereas I want them to come back to where they were located when the session timed out i.e. /userArea/thePage.
Is this possible?
If so how do I need to alter my config/app?
Here is my current config:
<beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<beans:property name="invalidSessionStrategy" ref="simpleRedirectInvalidSessionStrategy" />
</beans:bean>
<beans:bean id="simpleRedirectInvalidSessionStrategy" class="org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy">
<beans:constructor-arg name="invalidSessionUrl" value="/signin" />
<beans:property name="createNewSession" value="true" />
</beans:bean>
<http auto-config="true" use-expressions="true">
<custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" />
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/signin" authentication-failure-url="/signin?login_error=t" default-target-url="/userArea" />
<logout logout-url="/resources/j_spring_security_logout" logout-success-url="/signin" />
...
edit 1: Let me better specify my requirements:
When a user session times out, I want the user to be redirected to the saved request (the url they requested before being redirected to the signin page).
However, when they initially signin with the app, I want them to be redirected to the home page of the personal area.
Are my requirements possible to implement using solution suggested by Carsten (see below)?
You could set the always-use-default-target="true" in the form-login tag. This redirects the user to the url they where trying to access before being intercepted to login.
But this will be the standard behaviour and not only in the case of a session timeout. Depending on the application this might not be what you want.
Edit:
To do what you want you need to find a way to save the information on which page the user was when the session timedout. I don't know of any out of the box solution for this problem, since there is no state that indicates whether or not the user timed out or logged out manually.
What needs to be done ist to:
set a flag or save the page-url on session timeout
check in a custom AuthenticationSuccesHandler and redirect accordingling
If I would implement somehing like that I would most likely store the page-url. Also there are a few tricky things with this from an UX perspective. What happens if the saved page relies on a state achieved earlier? (I assume thats the reason you want the User to go to the default-url on normal login?) What happens if the user just does not log out shuts down sleeps for the night and logs in navigating to the login page (does the flag/page-url time out?)? etc.
In general I think it would be better use the always-use-default-target="true" since this adds the comfort of bookmarking any page and not having to navigate there at each login.
Looks like it's common issue for any Spring project.
Spring developers thought that this is undocumented behavior https://github.com/spring-projects/spring-security/issues/1981, my business users are thinking that it's a bug.
So, as a result we need to do some custom implementation)))
Personally for me it's a bug and after making a custom implementation I don't understand why it's not fixed at Spring Framework.
As in a lot of other cases we have no choice and just copy-paste SimpleRedirectInvalidSessionStrategy and add our custom code.
You can even more simplify this code(I just make a customization which can be used OOTB in Spring):
public class CustomInvalidSessionStrategy implements InvalidSessionStrategy {
private final Log logger = LogFactory.getLog(this.getClass());
private String destinationUrl = null;
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
private boolean createNewSession = true;
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.logger.debug("Starting new session (if required) and redirecting to '" + this.destinationUrl + "'");
if (this.createNewSession) {
request.getSession();
}
if (destinationUrl == null) {
this.redirectStrategy.sendRedirect(request, response, request.getRequestURI());
} else {
this.redirectStrategy.sendRedirect(request, response, this.destinationUrl);
}
}
public void setCreateNewSession(final boolean createNewSession) {
this.createNewSession = createNewSession;
}
public void setInvalidSessionUrl(final String invalidSessionUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(invalidSessionUrl), "url must start with '/' or with 'http(s)'");
this.destinationUrl = invalidSessionUrl;
}
}
And some extra configuration for Spring security:
<security:http ...>
...
<security:session-management invalid-session-strategy-ref="customInvalidSessionStrategy" />
...
</security:http>
<bean id="customInvalidSessionStrategy" class="com.custom.web.security.CustomInvalidSessionStrategy"/>

How to handle 404 with Spring Security?

I found only related topic for c# please don't blame me if I missed the resource.
It looks something like
/project/blablaentered and content with 404.
Effectively I just want to specify my own page when 404 page is thrown.
My security xml:
<security:http auto-config="true" use-expressions="true" >
<security:form-login login-processing-url="/static/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" default-target-url="/home"/>
<security:intercept-url pattern="/home" access="isAuthenticated()" />
<security:intercept-url pattern="/home/try" access="hasRole('ROLE_EDITOR')"/>
<security:access-denied-handler error-page="/accessDenied"/>
</security:http>
UPDATE: Please follow for solution: Custom 404 using Spring DispatcherServlet
The simplest way is probably enable an error-page element inside web.xml as long as you don't mind it being a plain JSP (ie, no controller). This way, URLs outside your DispatcherServlet which will generate a 404 from your servlet container will follow the same path as any URL that Spring is unable to map to a controller based on your configuration.
If this isn't good enough, you can define a #Exception method for a particular controller, or use a HandlerExceptionResolver.
You need add class:
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
// Do nothing
}

How can I set an Admin/Superuser role to have complete access using Spring Security?

I want to limit access to directories based on roles, e.g.,:
<intercept-url pattern="/foo/**" access="hasRole('ROLE_FOO')"/>
<intercept-url pattern="/bar/**" access="hasRole('ROLE_BAR')"/>
But I also want a superuser role that can access everything, e.g., :
<intercept-url pattern="/**" access="hasRole('ROLE_SUPERUSER')"/>
Is there a way to accomplish this other than using hasAnyRole, throughout? e.g.,
<!-- This seems ugly, with all the repeated references. OTOH, it's explicit -->
<intercept-url pattern="/foo/**" access="hasAnyRole('ROLE_FOO', 'ROLE_SUPERUSER')"/>
<intercept-url pattern="/bar/**" access="hasRole('ROLE_BAR') or hasRole('ROLE_SUPERUSER')"/>
It looks like a case for RoleHierarchy.
Though I can't find a good way how to configure it. Perhaps this rough approach would work:
public class RoleHierarchyInjectionBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof DefaultWebSecurityExpressionHandler) {
((DefaultWebSecurityExpressionHandler) bean).setRoleHierarchy(...);
}
return bean;
}
...
}
In this article there is a solution to a similar problem. You can ignore the part about JMX, what matters to you is the talk about AccessDecisionManager and AccessDecisionVoter. The idea is to register a custom AccessDecisionVoter that implements the if (superuser) grant access;logic.

Resources