Issues with automatic login with a custom user details service - spring

I'm having some issues with my implementation of spring security. I am using a custom user details service and when I attempt to signup a new account it doesn't seem to be logging in the user as it should. Here's the code that I'm attempting to use to login the user with the SignInUtils class
SignInUtils.java
package com.youthministry.controller;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
public class SignInUtils {
/**
* Programmatically signs in the user with the given the user ID.
*/
public static void signin(String userId) {
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userId, null, null));
}
}
LoginController.java
package com.youthministry.controller;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.WebRequest;
import com.youthministry.controller.SignInUtils;
import com.youthministry.controller.SignupForm;
import com.youthministry.domain.Role;
import com.youthministry.domain.User;
import com.youthministry.domain.UserProfile;
import com.youthministry.service.UserService;
#Controller
public class LoginController {
#Autowired
private UserService UserService;
#RequestMapping(value={"/", "/signin"})
public String signin() {
return "signin";
}
#RequestMapping(value="/signup", method=RequestMethod.GET)
public SignupForm signupForm(WebRequest request) {
Connection<?> connection = ProviderSignInUtils.getConnection(request);
if (connection != null) {
return SignupForm.fromProviderUser(connection.fetchUserProfile());
} else {
return new SignupForm();
}
}
#RequestMapping(value="/signup", method=RequestMethod.POST)
public String signup(#Valid SignupForm form, BindingResult formBinding, WebRequest request) {
if (formBinding.hasErrors()) {
return null;
}
User user = createUser(form, formBinding);
if (user != null) {
SignInUtils.signin(user.getUsername());
ProviderSignInUtils.handlePostSignUp(user.getUsername(), request);
return "redirect:/home";
}
return null;
}
// Internal helpers
private User createUser(SignupForm form, BindingResult formBinding) {
User user = new User();
user.setUsername(form.getUsername());
user.setPassword(form.getPassword());
UserProfile userProfile = new UserProfile();
userProfile.setFirstName(form.getFirstName());
userProfile.setLastName(form.getLastName());
user.setUserProfile(userProfile);
Role role = new Role();
role.setName("ROLE_USER");
user.getRoles().add(role);
UserService.addUser(user);
return user;
}
}
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<http pattern="/resources/css/**" security="none"/>
<http pattern="/resources/images/**" security="none"/>
<http pattern="/resources/scripts/**" security="none"/>
<http use-expressions="true" access-denied-page="/accessDenied.jsp" >
<form-login login-page="/signin" default-target-url="/home" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?error=bad_credentials"/>
<logout logout-success-url="/signin" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/group/**" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/group" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/home" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/**" access="permitAll" />
<intercept-url pattern="/auth/**" access="permitAll" />
<intercept-url pattern="/disconnect/facebook" access="permitAll" />
<remember-me user-service-ref="userDetailsService" />
<!--<session-management invalid-session-url="/invalidsession.jsp">
<concurrency-control max-sessions="1"
error-if-maximum-exceeded="true" />
</session-management>-->
<!-- Spring Social Security authentication filter -->
<custom-filter ref="socialAuthenticationFilter" before="PRE_AUTH_FILTER" />
</http>
<beans:bean id="customEncoder" class="com.youthministry.security.CustomPasswordEncoder" />
<beans:bean id="customUserService"
class="com.youthministry.security.CustomUserDetailService" >
</beans:bean>
<authentication-manager alias="authenticationManager" >
<authentication-provider user-service-ref="customUserService">
<password-encoder ref="customEncoder" />
</authentication-provider>
<!-- Spring Social Security authentication provider -->
<authentication-provider ref="socialAuthenticationProvider" />
</authentication-manager>
<jdbc-user-service id="userDetailsService"
data-source-ref="dataSource"
users-by-username-query="select username, password, true from User where username = ?"
authorities-by-username-query="select u.username username, r.name authority from user_roles u_roles, user u, roles r where u.username = ?"/>
<beans:bean id="textEncryptor" class="org.springframework.security.crypto.encrypt.Encryptors"
factory-method="noOpText" />
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"
factory-method="getInstance" />
</beans:beans>
If any other files may be needed to help let me know. Otherwise here's the link the github repo.
http://github.com/dmcquillan314/YouthMinistryHibernate.git
Thanks in advance for any assistance I'm still new to spring and trying to figure out a few issues regarding initial spring-security configuration. Any comments on the github project are also welcome.

I think the problem is that in SignInUtils.signin() you place an authentication token into the security context that doesn't hold any GrantedAuthority (the 3rd parameter of new UsernamePasswordAuthenticationToken() is null), and then redirect the request to a URL (/home) which is configured to require the ROLE_USER authority.

Related

Spring Security with overlapping configurations checks authorization in multiple <http> blocks

I have an application using Spring Security 4.2.18 with the security configured in xml. Right now I am introducing Spring Boot 2.5.4 in the project. Since upgrading, I have a problem with the configuration of security for some requests.
I have a block matching specific requests and one matching the rest of all requests.
<http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" >
<http-basic/>
<intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<csrf disabled="true"/>
</http>
<http pattern="/**" auto-config="true" use-expressions="true" create-session="always" disable-url-rewriting="true"
authentication-manager-ref="authenticationManager">
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/login"
username-parameter="j_username"
password-parameter="j_password"
authentication-failure-url="/login?login_error=t" default-target-url="/redirectlogin"/>
<logout logout-url="/resources/j_spring_security_logout"/>
...
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
...
</http>
<authentication-manager id="apiAuthenticationManager">
<authentication-provider user-service-ref="apiUserDetailsService">
</authentication-provider>
</authentication-manager>
The ApiUserDetailsService follows the specification:
#Service
#Transactional
public class ApiUserDetailsService implements UserDetailsService {
...
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
boolean foundAccount = apiConfig.getUsername().equals(username);
if (foundAccount) {
return new User(username, apiConfig.getPassword(), singletonList(new SimpleGrantedAuthority("ROLE_API")));
}
throw new UsernameNotFoundException("Could not finder user with name " + username);
}
}
If I make a request to something under /api and use incorrect Basic auth credentials, I previously got a 401 Unauthorized response. Since upgrading I get redirected to /login and end up in a redirect loop since the credentials are used there as well.
If I remove the <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/> in the second <http> block, I get the expected behaviour.
How can I configure my application so that my request doesn't try to authorize using the rules in the second <http> block? I have tried using an EntryPoint but it isn't called before the user is erroneously authorized using the method of the generic <http> block.
Have you tried putting <intercept-url pattern="/login*" access="permitAll" /> right above your <intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/> like:
In the second http block:
...
<intercept-url pattern="/login*" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
...
The problem wasn't that the configurations were overlapping but rather that the failed authentication was redirected to /login which in turn was handled by the second <http> configuration.
I solved it by creating an implementation of AuthenticationEntryPoint that sets the 401 Unathorized status on the response:
#Component
public class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
}
and in turn referencing to this in my basic authentication configuration:
<http pattern="/api/**" use-expressions="true" authentication-manager-ref="apiAuthenticationManager" >
<http-basic entry-point-ref="basicAuthenticationEntryPoint"/>
<intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<csrf disabled="true"/>
</http>

Automate Login to site using Spring Boot (Spring Security) and LDAP

I am working on a Spring Boot project, which uses LDAP in Spring Security for authentication.
I need to automate the login once the user hits the login page based on the roles in LDAP group provided in Spring Security.
If user has any role in the group mentioned in LDAP, then it must redirect to the corresponding page after login. (i.e page1 in my example).
I have been searching 2 days in a row for this for any online documentation or an example, but in vain. All I could find is using a jdbcDataSource or hard coding the username and password in Controller and later validating it when login or through Spring using web.xml. But not via LDAP. Any help would be much helpful.
This is how my Spring Security XML looks:
<?xml version="1.0" encoding="UTF-8" ?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/logout" access="permitAll" />
<intercept-url pattern="/webjars/**" access="permitAll" />
<intercept-url pattern="/page1" access="hasAnyRole('GP1','GP2')" />
<intercept-url pattern="/page2" access="hasAnyRole('GP1','GP2')" />
<intercept-url pattern="/page3" access="hasAnyRole('GP1','GP2')" />
<intercept-url pattern="/**" access="permitAll" />
<form-login default-target-url="/page1" login-page="/login"
always-use-default-target="true" />
<access-denied-handler error-page="/403.html" />
<csrf disabled="true" />
<logout logout-url="/logout" />
</http>
<authentication-manager alias="authenticationManager"
erase-credentials="false">
<authentication-provider ref="ldapAuthProvider" />
</authentication-manager>
<ldap-server id="contextSource" url="ldap://url"
manager-dn="mymanagerdn" manager-password="mymanagerpswd" />
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean id="bindAuthenticator"
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch" ref="userSearch" />
</beans:bean>
</beans:constructor-arg>
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<beans:constructor-arg ref="contextSource" />
<beans:constructor-arg value="myDCvalues" />
<beans:property name="searchSubtree" value="true" />
<beans:property name="ignorePartialResultException"
value="true" />
<beans:property name="groupSearchFilter" value="(member={0})" />
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0"
value="myDCvalues" />
<beans:constructor-arg index="1"
value="(sAMAccountName={0})" />
<beans:constructor-arg index="2" ref="contextSource" />
<beans:property name="searchSubtree" value="true" />
</beans:bean>
</beans:beans>
My WebController:
package com.myPackage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Controller
public class WebController extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/page1").setViewName("page1");
registry.addViewController("/page2").setViewName("page2");
registry.addViewController("/page3").setViewName("page3");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/403").setViewName("error/403");
}
#GetMapping("/page1")
public String page1(HttpSession session) {
return "page1";
}
#GetMapping("/page2")
public String page2(HttpSession session) {
return "page2";
}
#GetMapping("/page3")
public String page3(HttpSession session) {
return "page3";
}
#GetMapping("/login")
public String login() {
return "login";
}
#GetMapping("/403")
public String error403() {
return "error/403";
}
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("templates/");
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
private String getCredentials() {
String credential = null;
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
credential = userDetails.getUsername().toString();
return credential;
}
}
For your convenience, this answer comes with a complete and working sample, including an LDAP server, populated with users and groups and an integration test, so that you can run these tests yourself.
Assume you have the following user in LDAP
dn: cn=marissa,ou=Users,dc=test,dc=com
changetype: add
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: marissa
userPassword: koala
uid: 20f459e0-e30b-4d1f-998c-3ded7f769db1
mail: marissa#test.com
sn: Marissa
The username is marissa and the password is koala.
Let's start with the test case:
#Test
#DisplayName("ldap login works")
void doLogin() throws Exception {
mvc.perform(
MockMvcRequestBuilders.post("/login")
.param("username", "marissa")
.param("password", "koala")
.with(csrf())
)
.andExpect(status().is3xxRedirection())
.andExpect(authenticated())
;
}
From this test, we can deduce that
LDAP uses form login, username/password
The form has CSRF protection
So let's configure your Spring Boot application using Java config
The classic sample file, SecurityConfig.java
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
The security configuration doesn't change.
We want users to be fully authenticated
We want to use form login
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
;
}
That's it, next we configure LDAP, again in your SecurityConfig.java we do this by calling the AuthenticationManagerBuilder. This is a bean that Spring Security configures. So we can access it using #Autowired
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.contextSource()
.url("ldap://localhost:389")
.managerDn("cn=admin,dc=test,dc=com")
.managerPassword("password")
.and()
.userSearchBase("ou=Users,dc=test,dc=com")
.userSearchFilter("cn={0}")
.groupSearchBase("dc=test,dc=com")
.groupSearchFilter("member={0}")
;
}
and that's it. Now, I used some code I wrote from the Cloud Foundry UAA project to create an in memory LDAP server for my integration tests.
So when the mock MVC integration test starts up, it starts an LDAP server to run against.
It really is that simple. You can now expand this sample to map LDAP groups to Spring Security authorities.
The LDAP Sample is available in my community repo: https://github.com/fhanik/spring-security-community.git

How to configure one login page with Multiple landing pages which intercept with different url patterns in spring security 4

My requirement is, I have two different landing pages one for user and one for admin. This landing pages has to be appear based on the intercept url pattern which i configure in the spring security xml file. Both the landing page is having a hyperlink to login, when the user click on the login hyperlink of adminLayout.jsp it will load the same login page and when the user click on the login hyperlink of userLayout.jsp it will load the same login page by interacting with the controller for two different url patterns.The url patterns will be /admin and /user.
I stuck here.
How can i configure two different landing pages(adminLayout) and userLayout) in spring security. this tow landing pages is having the same login form, which i want to configure in the spring security form-login for both the url patterns and tow layouts.Before login only the landing page has to appear and then when the user click on the login hyperlink from two different pages it has to make user of spring security provided login-form functionality.Please help me out on this.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" requires-channel="http" />
<intercept-url pattern="/user**" access="hasRole('ROLE_USER')" requires-channel="http" />
<csrf disabled="true"/>
<access-denied-handler ref="accessDeniedHandler"/>
<!-- Here i want to configure the landing pages based on the intercept url pattern if the pattern is /admin i want to dispaly the adminLayout.
If the intercept url pattern is /user i want to display the userLayout. Both this Layout pages is having common login page which user will click from this layout pages.
if want to make use of spring secuiryt for form login..
-->
<form-login login-processing-url="/j_spring_security_check"
login-page="/sslogin" authentication-success-handler-ref="authenticationHandler"
authentication-failure-url="/fail2login?error=true"/>
<logout logout-success-url="/logout" invalidate-session="true" logout-url="/j_spring_security_logout" delete-cookies="JSESSIONID" />
<session-management>
<concurrency-control error-if-maximum-exceeded="true" max-sessions="1" expired-url="/fail2login" />
</session-management>
</http>
<beans:bean id="accessDeniedHandler" class="com.fss.portal.handlers.PortalAccessDeniedHandler">
<beans:property name="accessDeniedURL" value="403"></beans:property>
</beans:bean>
<beans:bean id="authenticationHandler" class="com.fss.portal.handlers.AuthenticationHandler">
</beans:bean>
<beans:bean id="customAuthenticationProvider" class="com.fss.portal.utility.CustomAuthenticationProvider">
<beans:property name="passwordEncoder" ref="bcryptEncoder"></beans:property>
</beans:bean>
<beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider">
</authentication-provider>
</authentication-manager>
I think you should try creating a AuthenticationEntryPoint implementation with multiple landing page support.
It could be something like this:
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
public class MultipleLandingPageEntryPoint extends LoginUrlAuthenticationEntryPoint
implements AuthenticationEntryPoint {
private Map<String, String> landingPages;
public MultipleLandingPageEntryPoint(String defaultLoginFormUrl, Map<String, String> landingPages) {
super(defaultLoginFormUrl);
this.landingPages = landingPages;
}
public MultipleLandingPageEntryPoint(String defaultLoginFormUrl) {
super(defaultLoginFormUrl);
}
public Map<String, String> getLandingPages() {
return landingPages;
}
public void setLandingPages(Map<String, String> landingPages) {
this.landingPages = landingPages;
}
#Override
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
for(String key : this.landingPages.keySet()){
RequestMatcher rm = new RegexRequestMatcher(key, null);
if(rm.matches(request)){
return this.landingPages.get(key);
}
}
// If not found in the map, return the default landing page through superclass
return super.determineUrlToUseForThisRequest(request, response, exception);
}
}
Then, in your security config, you must configure it:
<beans:bean id="authenticationMultiEntryPoint" class="com.xxx.yyy.MultipleLandingPageEntryPoint">
<beans:constructor-arg value="/user/landing.htm" />
<beans:property name="landingPages">
<beans:map>
<beans:entry key="/user**" value="/user/landing.htm" />
<beans:entry key="/admin**" value="/admin/landing.htm" />
</beans:map>
</beans:property>
</beans:bean>
And use it in your <security:http> element:
<security:http pattern="/admin/landing.htm" security="none" />
<security:http pattern="/user/landing.htm" security="none" />
<security:http auto-config="true" use-expressions="true"
entry-point-ref="authenticationMultiEntryPoint">
If you implement the AuthenticationEntryPoint extending LoginUrlAuthenticationEntryPoint (which I think it's a good idea) check additional parameters on it.
EDIT: I've just updated the class implementation, did not include the latest version
Create A Custom AuthenticationSuccessHandler like below
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response,
Authentication authentication)
throws IOException,
javax.servlet.ServletException {
if(authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN")) {
request.getRequestDispatcher("/admin").forward(request, response);
} else if (authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_USER")) {
request.getRequestDispatcher("/user").forward(request, response);
}
}
}
And configure it with form-login tag as following
<bean id="customAuthenticationSuccessHandler" class="CustomAuthenticationSuccessHandler" />
<form-login authentication-success-handler-ref="customAuthenticationSuccessHandler" ...>
UPDATE
Create a Controller mappings /landing point to it by <form-login login-page="/landing" .../>. This landing should have links to admin and user landing pages. Which can have links or forms to login.
You can remove protection from these landing pages.
<http pattern="/landing**" security="none"/>
<http pattern="/landing/admin**" security="none"/>
<http pattern="/landing/user**" security="none"/>
And you can write a Custom AuthenticationFailureHandler to redirect to correct login page.

Authentication using ldap in spring

Currently I work with datasource and spring in order to be autheticate,
this is my config in security-app-context.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 auto-config="true" use-expressions="true">
<intercept-url pattern="/administration/**" access="hasRole('ADMIN')" />
<intercept-url pattern="/citizen/**" access="hasRole('USER')" />
<form-login login-page="/index.htm" authentication-success-handler-ref="authenticationSuccessRedirecthandler"
default-target-url = "/citizen/test.htm"
authentication-failure-url="/index.htm?error=1"/>
<logout logout-success-url="/index.htm" />
</http>
<beans:bean class="com.test.redirect.CustomAuthenticationHandler" id="authenticationSuccessRedirecthandler"></beans:bean>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource" users-by-username-query="select username, password, enabled from users where username=?" authorities-by-username-query="select u.username, ur.authority from users u, user_roles ur where u.user_id = ur.user_id and u.username =? " />
</authentication-provider>
</authentication-manager>
</beans:beans>
and this is CustomAuthenticationHandler.java
import java.io.IOException;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
public class CustomAuthenticationHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
String targetUrl = "/test/page.htm";
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ADMIN")) {
getRedirectStrategy().sendRedirect(request, response, targetUrl);
} else {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
}
}
I want to know to correct config in order to authenticate not with datasource but with ldap
this is same parameter in my ldap :
Base Provider URL
ldap://192.168.0.88:389
Base DN
DC=MINISTER,DC=FR
Principal
CN=LDAP Requester,OU=Users,OU=Technical Accounts,OU=P9 Accounts,DC=MINISTER,DC=FR
Credentials
minister$9999
Users
Authentication Search Filter
(&(objectClass=person)(mail=#email_address#))
Users DN DC=MINISTER,DC=FR
Groups DN DC=MINISTER,DC=FR
updated :
I try with this code :
<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 auto-config="true" use-expressions="true">
<intercept-url pattern="/administration/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/citizen/**" access="hasRole('USER')" />
<intercept-url pattern="/menu/menu.htm" access="hasAnyRole('ROLE_ADMIN','USER')" />
<form-login login-page="/index.htm"
default-target-url = "/citizen/test.htm"
authentication-failure-url="/index.htm?error=1"/>
<logout logout-success-url="/index.htm" />
</http>
<beans:bean id="grantedAuthoritiesMapper" class="com.test.ActiveDirectoryGrantedAuthoritiesMapper"/>
<authentication-manager>
<authentication-provider ref="ldapActiveDirectoryAuthProvider" />
</authentication-manager>
<beans:bean id="ldapActiveDirectoryAuthProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<beans:constructor-arg value="DC=MINISTER,DC=TN" />
<beans:constructor-arg value="ldap://192.168.0.88:389" />
<beans:property name="authoritiesMapper" ref="grantedAuthoritiesMapper" />
<beans:property name="useAuthenticationRequestCredentials" value="true" />
<beans:property name="convertSubErrorCodesToExceptions" value="true" />
</beans:bean>
</beans:beans>
and this is a java class :
import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
/**
* Maps the groups defined in LDAP nomenclature to roles for a specific user.
*/
public class ActiveDirectoryGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper {
// Constants for group defined in LDAP
private static final String ROLE_ADMIN = "ADMIN";
public ActiveDirectoryGrantedAuthoritiesMapper() {
}
public Collection<? extends GrantedAuthority> mapAuthorities(
final Collection<? extends GrantedAuthority> authorities) {
Set<SecurityContextAuthority> roles = EnumSet.noneOf(SecurityContextAuthority.class);
for (GrantedAuthority authority : authorities) {
// authority.getAuthority() returns the role in LDAP nomenclature
if (ROLE_ADMIN.equals(authority.getAuthority())) {
roles.add(SecurityContextAuthority.ROLE_ADMIN);
}
}
return roles;
}
}
and this is SecurityContextAuthority.java class
import org.springframework.security.core.GrantedAuthority;
/**
* Maps the groups defined in LDAP to roles for a specific user.
*/
public enum SecurityContextAuthority implements GrantedAuthority {
// These roles are specified in the security context (security.xml) and are
// mapped to LDAP roles by the ActiveDirectoryGrantedAuthoritiesMapper
ROLE_ADMIN;
public String getAuthority() {
return name();
}
}
but when I test I have this error :
[org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider] (http-localhost-127.0.0.1-8080-3) Active Directory authentication failed: Supplied password was invalid
I'm doing this same thing, and my code is working. Looking over your code, I only spot one difference with my code. In your security config file, in the ldapActiveDirectoryAuthProvider bean, your two constructor args are:
<beans:constructor-arg value="DC=MINISTER,DC=TN" />
<beans:constructor-arg value="ldap://192.168.0.88:389" />
Mine follow a different construct; using your values, it would be like this:
<beans:constructor-arg value="192.168.0.88" />
<beans:constructor-arg value="ldap://192.168.0.88" />
You may need to pass the domain components too, but I'm wondering if your config needs to have both the domain and the URL passed in. Also, I've noticed that it seems to add the default port, so I leave it off my URL.

How to perform custom logic with Spring Security when user is remembered?

I'm using Spring Security 3 and I want to perform some logic (saving some data in the session) when the user is visiting the site and he's remembered. I extended the GenericFilterBean class, perform the logic in the doFilter method, then complete the filter chain by calling the chain.doFilter method. I inserted that filter after the "remember me" filter in the security.xml file.
But the problem is that the filter is executed on each page regardless of whether the user is remembered or not. Is there something wrong with the filter implementation or the position of the filter?
Is the filter chain by default executed on each page?
When making a custom filter should i add it to the web.xml too?
The filter class:
package projects.internal;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import projects.ProjectManager;
public class rememberMeFilter extends GenericFilterBean {
private ProjectManager projectManager;
#Autowired
public rememberMeFilter(ProjectManager projectManager) {
this.projectManager = projectManager;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
System.out.println("In The Filter");
Authentication auth = (Authentication) SecurityContextHolder
.getContext().getAuthentication();
HttpServletResponse response = ((HttpServletResponse) res);
HttpServletRequest request = ((HttpServletRequest) req);
// if the user is not remembered,do nothing
if (auth == null) {
chain.doFilter(request, response);
}
else {
// the user is remembered save some data in the session
System.out.println("User Is Remembered");
chain.doFilter(request, response);
}
}
}
The security.xml file:
<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">
</global-method-security>
<http use-expressions="true" >
<remember-me data-source-ref="dataSource"/>
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/images/**" filters="none" />
<intercept-url pattern="/scripts/**" filters="none" />
<intercept-url pattern="/styles/**" filters="none" />
<intercept-url pattern="/p/login" filters="none" />
<intercept-url pattern="/p/register" filters="none" />
<intercept-url pattern="/p/forgot_password" filters="none" />
<intercept-url pattern="/p/**" access="isAuthenticated()" />
<custom-filter after="REMEMBER_ME_FILTER" ref="rememberMeFilter" />
<form-login login-processing-url="/j_spring_security_check"
login-page="/p/login" authentication-failure-url="/p/login?login_error=1"
default-target-url="/p/dashboard" authentication-success-handler-ref="myAuthenticationHandler"
always-use-default-target="false" />
<logout/>
</http>
<beans:bean id="myAuthenticationHandler" class="projects.internal.myAuthenticationHandler" />
<beans:bean id="rememberMeFilter" class="projects.internal.rememberMeFilter" >
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<password-encoder hash="md5" />
<jdbc-user-service data-source-ref="dataSource" />
</authentication-provider>
</authentication-manager>
</beans:beans>
Any help?
I think your confusion comes from the fact that the Spring Security Filter Chain is a single ServletFilter, which contains it's own, internal, chain of SpringSecurityFilters. If you want to add a filter to the that chain you need to subclass SpringSecurityFIlter and implement it's doFilterHttp() method. In your case I might try to write a Pre-Authentication filter that does the logic you want. If no user object is found (hence there was none in the session) then it does some logic and then Spring Security goes on to authenticate the user. Check this out for a more detailed example Pre-Authentication Mechanism.

Resources