I am building spring mvc and spring security based web based application.
I have implemented Reset Password functionality.System Administrator will reset password of any user .Random generated password will be emailed to user and same will be updated in database.
Now I want whenever user login with random generated password, i want to force user to change its password.
Please have a look to my user TABLE.
userid bigint(20)
username varchar(20)
password varchar(65)
email varchar(50)
firstname varchar(20)
lastname varchar(20)
groupname varchar(50)
enabled tinyint(1)
credentialsNonExpired tinyint(1)
MY Authentication Provider
<!--
Configuring Authentication Provider to make use of spring security
provided Jdbc user management service
-->
<authentication-provider user-service-ref="jdbcUserService">
<!--
Configuring SHA-1 Password Encoding scheme to secure user credential
-->
<password-encoder ref="sha1PasswordEncoder" />
</authentication-provider>
</authentication-manager>
I have used JDBCUserDetailsService extending JDBCDaoImpl as jdbcUserService.
I want to set credentialNonExpired to false column of my user table when I am resetting password.
I am able to do that.
But when i login, spring security JDBCuserdetailsservice loadUserbyUsername getting only username,password,enabled columns and rest of all fields set to true.
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate().query(usersByUsernameQuery, new String[] {username}, new RowMapper<UserDetails>() {
public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
boolean enabled = rs.getBoolean(3);
return new User(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES);
}
});
}
But I want actual credentialNonExpired field which is set by reset password, so that spring security will throw CREDENTIALEXPIREDEXCEPTION.
I am achieving that by loading above method, but is there any other way to redirect user to change password page when they login with expired password.
Please tell me how can i do that ?
Quite late answer and I don't know if you're using Spring 2 or 3.
But in Spring 3 you can do it this way.
Include the following in your Spring security context:
<bean id="securityExceptionTranslationHandler" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.security.authentication.CredentialsExpiredException">/change_password_page</prop>
</props>
</property>
<property name="defaultFailureUrl" value="/login_generic_error_page"/>
</bean>
Of course you can map other specific authentication exceptions to other pages.
If you're using the form-login element, then you have to specify the authentication-failure-handler-ref attribute (and remove authentication-failure-url if used)
<security:form-login ... authentication-failure-handler-ref="securityExceptionTranslationHandler">
And final step is to create the change password page.
Keep in mind that the user is not authenticated when redirected to the change password page.
You can try subclassing SimpleUrlAuthenticationSuccessHandler and implement custom logic for checking password expiry. The reference to this SimpleUrlAuthenticationSuccessHandler could be passed to the form-login element in the application context.
Related
From my own inspection of the source and everything I have so far read, Spring Security's ActiveDirectoryLdapAuthenticationProvider (http://docs.spring.io/spring-security/site/docs/3.2.8.RELEASE/reference/htmlsingle/#ldap-active-directory) interacts with ActiveDirectory "in the clear"...passwords are transmitted using plain text.
I have see questions such as How does Spring Security LDAP protect password during Active Directory authentication?, but this only confirms my concern. My google-fu has not been good enough for me to have found a clear solution as yet.
I am working with a utility that is tightening up security significantly and I don't believe that my current use of ActiveDirectoryLdapAuthenticationProvider (with plaintext passwords) will be acceptable for much longer.
I currently use AD to authenticate a user and to validate that the presented user has been granted a role that lets them use the application making the request.
(I am not above ripping Spring Security out if I have to, but I strongly suspect that my managers' fear factor would increase quite significantly if I brought this to them, so I am looking for a minimal change, if such is at all possible...)
I believe the AD instance is Windows Server 2008 R2.
I am using ldap://...:389. My attempt to use ldaps://...:636 fails (but i don't currently have the exact message to hand; it's the weekend...apologies).
I can't believe that I am the only person out there with this need, so is there anyone out there with experience that can point me towards the 'canonical' solution?
Had used spring-ldap module to achieve your requirement.
What I did was :
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
</bean>
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="--ldaps url goes here" />
<property name="userDn"
value="--user dn goes here" />
<property name="password"
value="--password goes here" />
</bean>
In one of the service classes autowired ldapTemplate.
Had a method which looked something like this:
public boolean authenticate(final String username, final String password)
throws MyException {
//searchAttribute is bean attribute here
final String searchQuery = "(" + searchAttribute + "=" + username + ")";
boolean status = false;
AuthenticationErrorCallback callback = new SpringLdapAuthenticationErrorCallback();
try {
status = ldapTemplate.authenticate(basePath, searchQuery, password,
callback);
if (!status) {
LOGGER.error("Error while ldap authencation" + callback);
throw new MyException(
ErrorEnum.INVALID_USER.getCode(),
ErrorEnum.INVALID_USER.getDescription());
}
} catch (Exception ex) {
LOGGER.error("Error while ldap authencation" + ex);
throw new MyException(ErrorEnum.INVALID_USER.getCode(),
ErrorEnum.INVALID_USER.getDescription());
}
return status;
When using ldaps you should take care to add ldap servers certificate to your truststore. Since most of the time it would be a self signed certificate.
Also check if server support anonymous authentication.
If it does not then you will need a user which will first bind to ldap
and then authenticate the incoming request.
Hope this helps.
in our grails 2.4.4 project we want to use the Spring Security Core Plugin which we declare in BuildConfig.groovy this way:
compile ':spring-security-core:2.0-RC4'
For authentication against a REST service we implement a custom AuthenticationProvider:
class CustomAuthenticationProvider implements AuthenticationProvider {
def springSecurityService
Authentication authenticate(Authentication authentication) {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
String username = auth.getPrincipal()
String password = auth.getCredentials()
println "username:" + username
println "password:" + password
The CustomAuthenticationProvider.authenticate is invoked when entering a username and password in the Login form.
Unfortunately when accessing the form values via auth.getPrincipal() or auth.getCredentials() the returned value is always a String containing a value like this [Ljava.lang.String;#6ecd6d25
When debugging the application it seems that the values for the UsernamePasswordAuthenticationToken are not filled correctly from the request.
Does anybody know whats going wrong here?
When a user fails authentication, I want the username and password to be returned to the form.
I'm using the spring security core plugin with Grails and Spring Security LDAP. I've searched around for a while and have come up with zip. Any ideas?
From UsernamePasswordAuthenticationFilter javadoc:
If you want to retain the username, cache it in a customized AuthenticationFailureHandler
As for password there is no point to cache it, because it cannot be put back to form password field for security reasons.
For future reference, as the above answers are either too vague to be helpful to those of us who are just beginning to learn this framework for the first time (prompting such questions as: what's an AuthenticationFailureHandler? How do I implement one? How do I connect it to my existing infrastructure that was magically created by the <security:http> namespace handler?) or no longer work (the code to store the username in SPRING_SECURITY_LAST_USERNAME was removed from UsernamePasswordAuthenticationFilter as of version 3.1.0), here's a little more detail on the first answer:
An AuthenticationFailureHandler is used by the login process to decide what to do when authentication fails.
The default login form setup as provided by <security:http><security:form-login /></security:http> uses a SimpleUrlAuthenticationFailureHandler to perform the redirection to the login failed url (which defaults to /spring_security_login?login_error).
You can hook your own implementation in by using the authentication-failure-handler-ref attribute of your <form-login> element.
So, my implementation looks like this:
public class UsernameStoringUrlAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler
{
#Override
public void onAuthenticationFailure (HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException
{
request.getSession (true).setAttribute ("SPRING_SECURITY_LAST_USERNAME", request.getParameter ("j_username"));
super.onAuthenticationFailure (request, response, exception);
}
}
which is configured thus:
<security:form-login authentication-failure-handler-ref="authenticationFailureHandler" [...] />
...
<bean id="authenticationFailureHandler" class="my.package.UsernameStoringUrlAuthenticationFailureHandler" p:defaultFailureUrl="/LoginError" />
And then I can access the failed login username using the same approach as described in James Kleeh's answer here, but which no longer worked because of the change to the framework.
I was able to do the following to get the username back to the form: In LoginController.groovy:
render view: view, model: [postUrl: postUrl,
rememberMeParameter: config.rememberMe.parameter,
lastUsername: request.getSession().getAttribute("SPRING_SECURITY_LAST_USERNAME")]
This is a Spring Security question.
In my application, I have a User entity as a domain object. Users will be registered and will be logging in with credentials stored in the database. My User domain object contains implementation to support Spring UserDetails object.
The challenge is that I need an ability to log into the application even before the first user is created. In other words, I need to log in as 'admin' to create the 'admin' user.
To make sure my Spring setup is working, I'm currently returning the hardcoded admin user from SpringSecurityUserDetailsServiceImpl.loadUserByUsername(String userName).
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
User user=null;
try {
if("admin".equalsIgnoreCase(userName)) {
user=new User();
user.setUserName("ADMIN");
user.setPassword("adsf"); // assume there's a hash of a true password here
user.setStatus(UserStatus.ACTIVE);
user.setAccessLevel(UserAccessLevel.ADMINISTRATOR);
} else {
//user = userDAO.getUserByUserName(userName);
}
} catch(Throwable t) {
throw new UsernameNotFoundException("Unable to locate User with user name \"" + userName + "\".", t);
}
return user;
}
This works, so now, I'm looking for the right way to do it. One would be to define this default admin user credentials in a properties file and read that properties file within loadUserByUsername(String userName) to construct the admn user object. However, I'm hoping there is a way to do this within the Spring Security xml configuration. I tried security:user name="admin" password="admin" authorities="ADMINISTRATOR" but that apparently does not work when you have security:authentication-provider user-service-ref="customUserDetailsService"
My spring-security.xml
<security:http auto-config="true" use-expressions="true" access-denied-page="/denied">
<security:intercept-url pattern="/login.html" access="permitAll"/>
<security:intercept-url pattern="/style/**" access="permitAll"/>
<security:intercept-url pattern="/user**" access="hasRole('ADMINISTRATOR')"/>
<security:intercept-url pattern="/**" access="hasRole('AUTHOR')"/>
<security:form-login login-page="/login.html"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/login.html?failedAttempt=true"
default-target-url="/home.html"/>
<security:logout invalidate-session="true"
logout-success-url="/login"
logout-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>
<bean id="customUserDetailsService" class="com.modelsite.services.impl.SpringSecurityUserDetailsServiceImpl"/>
So the question is: how do I define a default admin user that is able to log in and do stuff. Please note, I do not want to handle this with sql imports at set up times.
You can have multiple authentication providers:
Use the first like you already did.
Add a second with fixed name, password and role for the admin.
(The order of both authentication providers is important; the second is only taken into account if the authentication is not found in the first.)
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
<security:authentication-provider>
<security:user-service>
<security:user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
#see also: Can I have multiple security contexts with spring security?
Personally, for the admin account I won't go with the basic Spring Security user service, mainly because it lacks the flexibility of a DB-based user management approach. Indeed, you probably don't want to have your admin credentials established once for all, since they can be guessed or stolen or simply forgotten.
Conversely, both password modification and recovery mechanisms should be put in place for all accounts, including the admin one (provided you use a trusted email account for password recovery, but this is a reasonable assumption).
Getting concrete, my approach is the following:
I use an AuthenticationManager where I inject a CustomUserDetailService
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="customUserDetailsService" >
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
<b:bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
which is the following
#Service
public class CustomUserDetailsService implements UserDetailsService{
#Autowired
#Qualifier("userDaoImpl")
private UserDao userDaoImpl;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
User user = userDaoImpl.loadByUsername(username);
if (user != null)
return user;
else
throw new UsernameNotFoundException(username + " not found.");
}
}
this works for all users, not only the admin.
Now it comes the problem of having the admin account full functional when the application starts. This is accomplished by using an initialization bean to be executed at startup, detailed in the following
#Component
public class Initializer {
#Autowired
private HibernateTransactionManager transactionManager;
#Autowired
#Qualifier("userDaoImpl")
private UserDao userDao;
#Autowired
private CredentialsManager credentialsManager;
private String resetPassword = "makeItHardToGuess";
private String adminUsername = "admin";
#PostConstruct
private void init()
{
//since we are executing on startup, we need to use a TransactionTemplate directly as Spring may haven't setup transction capabilities yet
TransactionTemplate trxTemplate = new TransactionTemplate(transactionManager);
trxTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
buildAdmin();
}
});
}
private void buildAdmin()
{
//here I try to retrieve the Admin from my persistence layer
ProfiledUser admin = userDao.loadByUsername(adminUsername);
try
{
//If the application is started for the first time (e.g., the admin is not in the DB)
if(admin==null)
{
//create a user for the admin
admin = new ProfiledUser();
//and fill her attributes accordingly
admin.setUsername(adminUsername);
admin.setPassword(credentialsManager.encodePassword(resetPassword));
admin.setAccountNonExpired(true);
admin.setAccountNonLocked(true);
admin.setCredentialsNonExpired(true);
admin.setEnabled(true);
admin.setEulaAccepted(true);
Authority authority = new Authority();
authority.setAuthority("ROLE_ADMIN");
admin.getAuthorities().add(authority);
}
//if the application has previously been started (e.g., the admin is already present in the DB)
else
{
//reset admin's attributes
admin.setPassword(credentialsManager.encodePassword(resetPassword));
admin.getAuthorities().clear();
Authority authority = new Authority();
authority.setAuthority("ROLE_ADMIN");
admin.getAuthorities().add(authority);
admin.setAccountNonExpired(true);
admin.setAccountNonLocked(true);
admin.setCredentialsNonExpired(true);
admin.setEnabled(true);
}
userDao.save(admin);
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Errors occurred during initialization. System verification is required.");
}
}
}
please note that the #PostConstruct annotation does not guarantee that spring has its transaction services available, that's why I had to manage the transaction my own. Please refer to this for more details.
The challenge is that I need an ability to log into the application even before the first user is created. In other words, I need to log in as 'admin' to create the 'admin' user.
The way I deal with this problem is to put some smarts into my custom UserDetailsService class and/or its DAO class. When it detects that it has been started with empty user details tables (or something), it initializes them with some user details entries that it reads from a configuration file. This allows you to:
load the initial admin account into your production system's user details store
load a bunch of test accounts into your test system's user details store for automated unit and system testing.
If that's too much work, just create some SQL statements to insert the relevant rows for the admin command and run them using your database's interactive SQL shell.
Embedding the admin account into your source code is a bad idea because:
anyone who can see your sourcecode can see the password (unless you use a hash),
it means that you need to modify and recompile the code to change the password, and
it means that you'll use the same password in testing and production (unless you add that distinction to your code as well).
These all raise security issues.
the answer by MaVVamaldo is cool (gave my +1 vote already) apart from the Initializer class. That class is great to initialise the database but it should avoid hard-coding the admin credentials which is unsafe as the source code can be easily retrieved (and it's what the original question asked to avoid in the first place).
A better solution IMHO would be to load the hashed credentials from a .properties file (to which you restrict the access via chmod or similar).
for this to work you need to have the following in your security-context.xml
<authentication-manager>
<authentication-provider>
<password-encoder hash="sha">
<salt-source user-property="username"/>
</password-encoder>
<user-service properties="classpath:/users.properties" />
</authentication-provider>
</authentication-manager>
where the .properties file looks like this:
bob=4f393f2314f75650ee50844d8e4f016ab5b3468f,ROLE_ADMIN,enabled
the salt is the username so you calculate it over the string password{username}
as explained.
I'm trying to implement a "remember me" functionality in my website using Spring. The cookie and entry in the persistent_logins table are getting created correctly. Additionally, I can see that the correct user is being restored as the username is displayed at the top of the page.
However, once I try to access any information for this user when they return after they were "remembered", I get a NullPointerException. It looks as though the user isn't being set in the session again.
My applicationContext-security.xml contains the following:
<remember-me data-source-ref="dataSource" user-service-ref="userService"/>
...
<authentication-provider user-service-ref="userService" />
<jdbc-user-service id="userService" data-source-ref="dataSource"
role-prefix="ROLE_"
users-by-username-query="select email as username, password, 1 as ENABLED from user where email=?"
authorities-by-username-query="select user.id as id, upper(role.name) as authority from user, role, users_roles where users_roles.user_fk=id and users_roles.role_fk=role.name and user.email=?"/>
I thought it may have had something to do with users-by-username query but surely login wouldn't work correctly if this query was incorrect?
Any help on this would be greatly appreciated.
Thanks,
gearoid.
Can you please include the entire stack trace of the exception? I suspect that because you have not set the key attribute on the remember-me configuration that you specified above that the token is not being set on the SecurityContextHolder.
To see details of how Remember Me works you should take a look at the source for the RememberMeAuthenticationFilter. You can find that source here (directly):
http://grepcode.com/file/repo1.maven.org/maven2/org.springframework.security/spring-security-web/3.0.2.RELEASE/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilter.java
RememberMeAuthenticationFilter is going to call in the RememberMeAuthenticationProvider as a result of:
rememberMeAuth = authenticationManager.authenticate(rememberMeAuth);
Inside the authenticate method you can see that it will throw an exception if you do not specify a key:
if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {
throw new BadCredentialsException(messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
"The presented RememberMeAuthenticationToken does not contain the expected key"));
}
The key can literally be any string "your-company-name-{GUID}" or something like that. So then your remember-me would look more like this:
<remember-me key="your-company-name-rmkey-aWeFFTgxcv9u1XlkswUUiPolizxcwsqUmml" token-validity-seconds="3600" data-source-ref="dataSource"/>
Setting the token-validity is a really good idea which you should do.
Grant