这是一个Spring Security问题。
在我的应用程序中,我有一个User实体作为域对象。用户将被注册,并将使用存储在数据库中的凭据登录。我的用户域对象包含支持Spring UserDetails对象的实现。
挑战在于我需要能够在创建第一个用户之前登录应用程序。换句话说,我需要以“admin”身份登录才能创建“admin”用户。
为了确保我的Spring设置正常工作,我目前正在从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;
}
这是有效的,所以现在,我正在寻找正确的方法来做到这一点。一种方法是在属性文件中定义此默认管理用户凭据,并在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导入来处理这个问题。
答案 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。