默认管理员用户(Spring 3,Spring Security)

时间:2012-01-06 04:14:36

标签: spring-security

这是一个Spring Security问题。

在我的应用程序中,我有一个User实体作为域对象。用户将被注册,并将使用存储在数据库中的凭据登录。我的用户域对象包含支持Spring UserDetails对象的实现。

挑战在于我需要能够在创建第一个用户之前登录应用程序。换句话说,我需要以“admin”身份登录才能创建“admin”用户。

为了确保我的Spring设置正常工作,我目前正在从SpringSecurityUserDetailsS​​erviceImpl.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;
}

这是有效的,所以现在,我正在寻找正确的方法来做到这一点。一种方法是在属性文件中定义此默认管理用户凭据,并在loadUserByUsername(String userName)中读取该属性文件以构造admn用户对象。但是,我希望有一种方法可以在Spring Security xml配置中执行此操作。我尝试security:user name="admin" password="admin" authorities="ADMINISTRATOR",但是当你有security:authentication-provider user-service-ref="customUserDetailsService"

时,这显然不起作用

我的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"/>

所以问题是:如何定义能够登录并执行操作的默认管理员用户。请注意,我不想在设置时使用sql导入来处理这个问题。

4 个答案:

答案 0 :(得分:3)

您可以拥有多个身份验证提供程序:

  • 像你已经做的那样使用第一个。
  • 为管理员添加具有固定名称,密码和角色的第二个。

(两个身份验证提供程序的顺序很重要;如果在第一个身份验证服务器中找不到身份验证,则只考虑第二个。

<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还:Can I have multiple security contexts with spring security?

答案 1 :(得分:1)

就个人而言,对于管理员帐户,我不会使用基本的Spring Security用户服务,主要是因为它缺乏基于数据库的用户管理方法的灵活性。实际上,您可能不希望一劳永逸地建立您的管理员凭证,因为它们可能被猜到或被盗或被遗忘。

相反,密码修改和恢复机制应该适用于所有帐户,包括管理员帐户(前提是您使用受信任的电子邮件帐户进行密码恢复,但这是一个合理的假设)

具体,我的方法如下: 我使用AuthenticationManager注入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" />

以下是

    @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.");
        }
    }

这适用于所有用户,而不仅仅是管理员。

现在它来了在应用程序启动时让管理员帐户完全正常运行的问题。这是通过使用初始化 bean在启动时执行来完成的,详见以下

    @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.");
            }
        }
    }

请注意@PostConstruct注释并不能保证spring的事务服务可用,这就是我必须自己管理事务的原因。有关详细信息,请参阅this

答案 2 :(得分:0)

  

挑战在于我需要能够在创建第一个用户之前登录应用程序。换句话说,我需要以“admin”身份登录才能创建“admin”用户。

我处理这个问题的方法是将一些智能放入我的自定义UserDetailsService类和/或其DAO类中。当它检测到它已使用空的用户详细信息表(或其他内容)启动时,它会使用从配置文件中读取的某些用户详细信息条目对其进行初始化。这允许您:

  • 将初始管理员帐户加载到生产系统的用户详细信息存储
  • 将一堆测试帐户加载到测试系统的用户详细信息存储中,以进行自动化单元和系统测试。

如果这工作太多,只需创建一些SQL语句来插入admin命令的相关行,并使用数据库的交互式SQL shell运行它们。


将管理员帐户嵌入到源代码中是一个坏主意,因为:

  • 任何能看到您的源代码的人都可以看到密码(除非您使用哈希),
  • 表示您需要修改并重新编译代码以更改密码,
  • 这意味着您将在测试和制作中使用相同的密码(除非您在代码中添加 区别)。

这些都会引发安全问题。

答案 3 :(得分:0)

MaVVamaldo的答案很酷(除了初始化程序类之外已经给我+1票)。该类很适合初始化数据库,但它应该避免硬编码不安全的管理员凭证,因为源代码可以很容易地检索(这是原始问题首先要避免的)。

更好的解决方案IMHO将从.properties文件加载散列凭据(您通过chmod或类似方式限制访问)。 为此,您需要在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>

.properties文件如下所示:

bob=4f393f2314f75650ee50844d8e4f016ab5b3468f,ROLE_ADMIN,enabled

salt是用户名,因此您可以通过字符串password{username}计算它 为explained