I have following test-config.xml
<authentication-manager alias="authenticationManager">
<authentication-provider ref="testProvider" />
<authentication-provider>
<user-service>
<user name="department1000" password="password" authorities="ROLE_1000" />
<user name="user" password="password2" authorities="ROLE_ALL_DEPT_ACCESS" />
<user name="user1" password="password3" authorities="ROLE_STUDENT" />
</user-service>
</authentication-provider>
</authentication-manager>
<beans:bean id="testProvider" class="org.springframework.security.authentication.TestingAuthenticationProvider">
</beans:bean>
I need a method that simulates authentication and giving the role:
protected void simulateRole(String role) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(role));
token = new TestingAuthenticationToken("username","password", authorities);
securityContext.setAuthentication((getAuthenticationManager().authenticate(token)));
Then I need to call the #PreAuthorized anotated controller method for test:
#Test(expected = AccessDeniedException.class)
public void testShowAccessDenied() {
super.simulateRole("ROLE_STUDENT");
controller.show(new ModelMap(), super.getAuthenticationPrincipal(), Locale.getDefault(), new D(), new E());
super.getSecurityContext().getAuthentication().getDetails();
I think I'm not setting the required Principal right, since test is not throwing AccessDeniedException
public Principal getAuthenticationPrincipal() {
return (Principal) securityContext.getAuthentication().getDetails();
Changing the type of controller method arguments would cause a lot of mess. Any way to get this working?
First you are missing the global method security from your configuration. You should add the following to test-config.xml:
<global-method-security pre-post-annotations="enabled" />
NOTE: For this to work in a servlet environment, you need to ensure to add the global-method-security tag to your DispatcherServlet config and not the root configuration as described in the FAQ
I'm assuming your controller does not implement an interface, so you will need to ensure that you have cglib on your classpath to support class based proxies. If you are using Spring 4.x+ you can add objenesis to your classpath and your proxied classes no longer need default constructors.
Second you need to ensure the controller in your test was created by Spring. When Spring creates the controller it uses the information from to proxy the class and add the security to it.
If you are still having issues, please post your complete test that fails, the method you are testing on the controller, and complete test code.
Related
Trying to use Spring security for authentication process, but getting Bad credentials exception.here is how I have defined things in my spring-security.xml file
<beans:bean id="passwordEncoder"
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
</beans:bean>
<beans:bean id="passwordEncoder"
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
</beans:bean>
<authentication-manager id="customerAuthenticationManager">
<authentication-provider user-service-ref="customerDetailsService">
<password-encoder hash="sha" />
</authentication-provider>
</authentication-manager>
customerDetailsService is our own implementation being provided to spring security.In my Java code while registering user, I am encoding provided password before adding password to Database something like
import org.springframework.security.authentication.encoding.PasswordEncoder;
#Autowired
private PasswordEncoder passwordEncoder;
customerModel.setPassword(passwordEncoder.encodePassword(customer.getPwd(), null));
When I tried to debug my application, it seems Spring is calling AbstractUserDetailsAuthenticationProvider authenticate method and when its performing additionalAuthenticationCheck with additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication); of DaoAuthenticationProvider, it throwing Bad credential exception at following point
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"), userDetails);
}
I am not sure where I am doing wrong and what needs to be done here to put things in right place, I already checked customer is there in database and password is encoded.
Alter your password-encoder declaration from
<password-encoder hash="sha" />
to
<password-encoder ref="passwordEncoder" />
Check if your customerDetailsService loads user credential from database.
If you still getting bad credentials exception: add erase-credentials="false" to your authenticationManager.
<authentication-manager id="customerAuthenticationManager" erase-credentials="false">
<authentication-provider user-service-ref="customerDetailsService">
<password-encoder hash="sha" />
</authentication-provider>
</authentication-manager>
I have a SpringMVC web application that needs to authenticate to a RESTful web service using Spring Security by sending the username and password. When an user is logged, a cookie needs to be set to the user's browser and in the subsequent calls the user session is validated with another RESTful web service by using the cookie.
I've been looking everywhere, but I have not been able to find a good example on how to accomplish this, and all my attempts have been in vain.
Here is what I have in mind:
I can have two authentication-providers declared, the first checks the cookie, and if it fails for any reason it goes to the second one which checks with the username and password (will fail too if there is no username and password in that request).
Both services return the authorities of the user each time, and spring security is "stateless".
On the other hand, I have questioned myself if this approach is correct, since it's been so difficult to find an example or somebody else with the same problem. Is this approach wrong?
The reason why I want to do this instead of just JDBC authentication is because my whole web application is stateless and the database is always accessed through RESTful web services that wrap a "petitions queue", I'd like to respect this for user authentication and validation too.
What have I tried so far? I could paste the long long springSecurity-context.xml, but I'll just list them instead for now:
Use a custom authenticationFilter with a authenticationSuccessHandler. Obviously doesn't work because the user is already logged in this point.
Make an implementation of entry-point-ref filter.
Do a custom-filter in the position BASIC_AUTH_FILTER
Make a custom Authentication Provider (Struggled a lot with no luck!). I'm retrying this while I get some answers.
I was starting to use CAS when I decided to write a question instead. Maybe in the future I can consider having a CAS server in my webapp, however for the moment, this feels like a huge overkill.
Thanks in advance!
BTW, I'm using Spring Security 3.1.4 and Spring MVC 3.2.3
EDIT: I WAS ABLE TO DO IT THANKS TO #coder ANSWER
Here is some light on what I did, I'll try to document all this and post it here or in a blog post sometime soon:
<http use-expressions="true" create-session="stateless" entry-point-ref="loginUrlAuthenticationEntryPoint"
authentication-manager-ref="customAuthenticationManager">
<custom-filter ref="restAuthenticationFilter" position="FORM_LOGIN_FILTER" />
<custom-filter ref="restPreAuthFilter" position="PRE_AUTH_FILTER" />
<intercept-url pattern="/signin/**" access="permitAll" />
<intercept-url pattern="/img/**" access="permitAll" />
<intercept-url pattern="/css/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
</http>
<authentication-manager id="authManager" alias="authManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<beans:bean id="restPreAuthFilter" class="com.company.CustomPreAuthenticatedFilter">
<beans:property name="cookieName" value="SessionCookie" />
<beans:property name="checkForPrincipalChanges" value="true" />
<beans:property name="authenticationManager" ref="authManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider"
class="com.company.CustomPreAuthProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService" />
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="userDetailsService" class="com.company.CustomUserDetailsService" />
<beans:bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg value="/signin" />
</beans:bean>
<beans:bean id="customAuthenticationManager"
class="com.company.CustomAuthenticationManager" />
<beans:bean id="restAuthenticationFilter"
class="com.company.CustomFormLoginFilter">
<beans:property name="filterProcessesUrl" value="/signin/authenticate" />
<beans:property name="authenticationManager" ref="customAuthenticationManager" />
<beans:property name="authenticationFailureHandler">
<beans:bean
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=t" />
</beans:bean>
</beans:property>
</beans:bean>
And the Custom Implementations are something like this:
// Here, the idea is to write authenticate method and return a new UsernamePasswordAuthenticationToken
public class CustomAuthenticationManager implements AuthenticationManager { ... }
// Write attemptAuthentication method and return UsernamePasswordAuthenticationToken
public class CustomFormLoginFilter extends UsernamePasswordAuthenticationFilter { ... }
// Write getPreAuthenticatedPrincipal and getPreAuthenticatedCredentials methods and return cookieName and cookieValue respectively
public class CustomPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter { ... }
// Write authenticate method and return Authentication auth = new UsernamePasswordAuthenticationToken(name, token, grantedAuths); (or null if can't be pre-authenticated)
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider{ ... }
// Write loadUserByUsername method and return a new UserDetails user = new User("hectorg87", "123456", Collections.singletonList(new GrantedAuthorityImpl("ROLE_USER")));
public class CustomUserDetailsService implements UserDetailsService { ... }
you can define a custom pre-auth filter by extending
AbstractPreAuthenticatedProcessingFilter.
In your implementation of
getPreAuthenticatedPrincipal() method you can check if cookie exists
and if it exists return cookie name is principal and cookie value in
credentials.
Use PreAuthenticatedAuthenticationProvider and provide your custom preAuthenticatedUserDetailsService to check if cookie is vali, if its valid also fetch granted authorities else throw AuthenticationException like BadCredentialsException
For authenticating user using username/password, add a form-login filter, basic-filter or a custom filter with custom authentication provider (or custom userdetailsService) to validate user/password
In case cookie exists, pre auth filter will set authenticated user in springContext and your username./password filter will not be called, if cookie is misisng/invalid, authentication entry point will trigger the authentication using username/password
Hope it helps
I am using spring based authentication by implementing UserDetailsService. Following is my spring config
<authentication-manager>
<authentication-provider user-service-ref="authenticationService">
<password-encoder ref="passwordEncoder">
<salt-source ref="authenticationService"/>
</password-encoder>
</authentication-provider>
</authentication-manager>
My authentication service looks like:
public class AuthenticationServiceImpl implements AuthenticationService, UserDetailsService, SaltSource {
....
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
....
return new org.springframework.security.core.userdetails.User(username, user.getPassword(), true, true, true, true, authorities);
}
}
Now the problem is when I create a new thread from one of my spring controllers. How do I authenticate my user in that thread ?
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(loadUserByUsername), password));
I found a better solution, to set it in configuration itself. Using following code:
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass"
value="org.springframework.security.core.context.SecurityContextHolder"/>
<property name="targetMethod" value="setStrategyName"/>
<property name="arguments"><b:list><b:value>MODE_INHERITABLETHREADLOCAL</value></list></property>
</bean>
Works like a charm.
How I get remember me value when login failed and reopen the login page?
Can i get the value of _spring_security_remember_me on controller?
I just need to keep the value of the checkbox when login error occurs!
You can try the following solution:
1. insert custom filter into spring security filter chain
2. inside this filter obtain http session and store there the value of request parameter
As we change the login form (adding another parameter) we need to customize spring representation of login form and spring login processing filter.
Here is the configuration:
<authentication-manager alias="authenticationManager"/>
<beans:bean id="myFilter" class="test.MyAuthenticationProcessingFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
<beans:property name="defaultTargetUrl" value="/initialize.action"/>
<beans:property name="authenticationFailureUrl" value="/login_failed.action"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
<beans:property name="filterProcessesUrl" value="/perform_login"/>
</beans:bean>
<beans:bean id="entryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<beans:property name="loginFormUrl" value="/login.action"/>
</beans:bean>
MyAuthenticationProcessingFilter extends spring's org.springframework.security.ui.webapp.AuthenticationProcessingFilter, wraps attemptAuthentication method obtaining request parameter and storing it inside http session. This class is written just to show the idea, for better practice browse AuthenticationProcessingFilter code for username and password parameters.
public class MyAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request)
throws AuthenticationException {
String param = request.getParameter("_spring_security_remember_me");
HttpSession session = request.getSession();
if (session != null || getAllowSessionCreation()) {
session.setAttribute("_spring_security_remember_me", param);
}
return super.attemptAuthentication(request);
}
}
You may notice that "myFilter" and "entryPoint" beans together define parameters that are otherwise defined by element inside . You use when you want the default behavior. But in our case we use custom beans, so you should remove element completely.
Now we need to tell use our beans. "myFilter" bean is passed to spring chain by using element inside bean definition:
<beans:bean id="myFilter" class="test.MyAuthenticationProcessingFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
...
</beans:bean>
"entryPoint" is passed to using attribute:
<http entry-point-ref="entryPoint">
...
<!-- no form-login here -->
</http>
your question is a bit unclear, or you have a wrong image of how remember me with spring security works. Read the Spring Security Reference Chapter 11 "Remember-Me Authentication"
Briefly it works this way:
If a user log in successfully with his user name and password and have enabled the remember me checkbox, Spring Security will create a cookie that verify the user and "send" it to the user
Not logged in User request a secured page (Authentication required) spring will check if he as a valid cookie.
If he has such a cookie spring security will "login" him "automatically" and show him the page
If he has no valid cookie spring will forward him to the login page (see above)
I hope this helps you.
I'm trying to perform integration of my GWT application and Spring Security. And when I add #PreAuthorize("hasRole('ROLE_USER')") annotation to the method of my DAO class following exception appears:
No unique bean of type [server.dao.ElementServiceDAO] is defined:
expected single bean but found 0
DaoServiceLocator can't find DAO bean, but in debug mode I see elementServiceDAO bean in ApplicationContext instance.
My DAO class looks like this:
#Service
public class ElementServiceDAO extends EntityDAO {
#PreAuthorize("hasRole('ROLE_USER')")
#SuppressWarnings("unchecked")
public Layer getFullRenderingTopology() {
...
}
}
DAO service locator's code:
public class DaoServiceLocator implements ServiceLocator {
#Override
public Object getInstance(final Class<?> clazz) {
try {
final HttpServletRequest request = RequestFactoryServlet
.getThreadLocalRequest();
final ServletContext servletContext = request.getSession()
.getServletContext();
final ApplicationContext context = WebApplicationContextUtils
.getWebApplicationContext(servletContext);
return context.getBean(clazz);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
}
applicationContext-security.xml:
<authentication-manager>
<authentication-provider>
<user-service>
<user name="operator" password="operator" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="guest" password="guest" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
<global-method-security pre-post-annotations="enabled" />
Please give me any advice!
When ElementServiceDAO implements an interface (in your case - transitively via EntityDAO), Spring by default creates an interface-based proxy to apply security aspects. So, elementServiceDAO in your application context is a proxy that isn't instance of ElementServiceDAO, therefore it cannot be retrieved by type.
You either need to
force creation of target-class-based proxies as follows
<global-method-security
pre-post-annotations="enabled" proxy-target-class = "true" />
or create a business interface for ElementServiceDAO and use that interface instead of implementation class.
See also:
7.6 Proxying mechanisms