Default Admin (Spring 3, Spring Security)

This is a Spring security issue.

In my application, I have a User object as a domain object. Users will be registered and will be registered with the credentials stored in the database. The My User domain object contains an implementation to support the Spring UserDetails object.

The problem is that I need the ability to enter the application before creating the first user. In other words, I need to log in as "admin" to create the user "admin".

To make sure my Spring installation works, I am currently returning a hard-coded 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 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 this. You could define these default administrator credentials in the properties file and read this properties file in the loadUserByUsername (String userName) file to create the admn user object. However, I hope there is a way to do this in the Spring Security xml configuration. I tried security:user name="admin" password="admin" authorities="ADMINISTRATOR" , but this does not seem to work if 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 this: how to determine the default administrator who can log in and do something useful. Please note: I do not want to handle this with sql imports during installation.

+4
source share
4 answers

You can have several authentication providers:

  • Use the first one, as you have already done.
  • Add a middle name with a fixed name, password and administrator role.

(The order of both authentication providers is important, and the second is only taken into account if 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 using spring security?

+3
source

Personally, for the administrator account, I will not use the basic Spring Security user service, mainly because it lacks the flexibility of a database-based user management approach. Indeed, you probably do not want your administrator credentials to be set once for everyone, as they can be guessed or stolen or just forgotten.

Conversely, for all accounts, including administrative, both password modification and password recovery mechanisms should be created , including administrative (provided that you use a reliable email account to recover the password, but this is a reasonable assumption).

Having a specific approach, my approach is this: I use AuthenticationManager , where I insert 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 next

  @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 just the administrator.

Now there is a problem with the full functionality of the administrator account when starting the application . This is achieved by initializing the bean to start at startup, described in detail below.

  @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 (eg, 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 (eg, the admin is already present in the DB) else { //reset admin 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."); } } } 

note that the @PostConstruct annotation @PostConstruct not guarantee that Spring has its own transactional services, so I had to manage the transaction myself. See more details.

+1
source

The problem is that I need the ability to enter the application before creating the first user. In other words, I need to log in as "admin" to create the user "admin".

The way I solve this problem is to add some smart elements to my own UserDetailsService class and / or its DAO class. When it discovers that it was started with empty tables of user details (or something else), it initializes them with some user data records that it reads from the configuration file. This allows:

  • upload the initial administrator account in the repository of information about your production system.
  • upload a bunch of test accounts to the user information store of your test system to automatically test modules and systems.

If this is too much, just create some SQL statements to insert the appropriate lines for the admin command and run them using the SQL database interactive shell.


Embedding an administrator account in the source code is a bad idea because:

  • anyone who can see your source code can see the password (if you do not use a hash),
  • this means that you need to change and recompile the code to change the password, and
  • this means that you will use the same password for testing and production (unless you add this difference to your code).

All these problems increase safety.

0
source

MaVVamaldo's answer is cool (already gave my +1 vote), except for the Initializer class. This class is great for initializing a database, but it should avoid hard-coding admin credentials, which is unsafe since the source code can be easily extracted (and this is what the original question asked to avoid first).

IMHO's best solution would be to load hashed credentials from a .properties file (to which you restrict access via chmod or the like). for this 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 is as follows:

 bob=4f393f2314f75650ee50844d8e4f016ab5b3468f,ROLE_ADMIN,enabled 

salt is the username, so you calculate it by the string password{username} as explained .

0
source

Source: https://habr.com/ru/post/1389586/


All Articles