Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default admin user (Spring 3, Spring Security)

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.

like image 335
jacekn Avatar asked Jan 06 '12 04:01

jacekn


People also ask

What is the default username for Spring Security?

As of Spring Security version 5.7. 1, the default username is user and the password is randomly generated and displayed in the console (e.g. 8e557245-73e2-4286-969a-ff57fe326336 ).

What is default password for Spring Security?

The default user name is “user” and the password is generated every time the application is restarted. The generated security password is shown in the startup log of the spring boot application console. The default password is a uuid format. The default password for each restart is changed.

Does Spring Security use default login form?

Spring security secures all HTTP endpoints by default. A user has to login in a default HTTP form. To enable Spring Boot security, we add spring-boot-starter-security to the dependencies.


2 Answers

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?

like image 178
Ralph Avatar answered Oct 26 '22 11:10

Ralph


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.

like image 21
MaVVamaldo Avatar answered Oct 26 '22 11:10

MaVVamaldo