使用用户名或电子邮件的 Spring Security
Posted
技术标签:
【中文标题】使用用户名或电子邮件的 Spring Security【英文标题】:Spring Security using both username or email 【发布时间】:2012-12-16 20:41:03 【问题描述】:我在我的 Spring MVC
应用程序中使用 Spring Security。
JdbcUserDetailsManager
使用以下验证查询进行初始化:
select username, password, enabled from user where username = ?
当局正在这里加载:
select u.username, a.authority from user u join authority a on u.userId = a.userId where username = ?
我想让它让用户可以使用用户名和电子邮件登录。有没有办法修改这两个查询来实现?还是有更好的解决方案?
【问题讨论】:
【参考方案1】:您可以分别在users-by-username-query
和authorities-by-username-query
属性中的<jdbc-user-service>
标签中定义自定义查询。
<jdbc-user-service data-source-ref="" users-by-username-query="" authorities-by-username-query=""/>
更新
您可以创建实现org.springframework.security.core.userdetails.UserDetailsService
的类并将您的应用程序配置为将其用作身份验证源。在您的自定义 UserDetails 服务中,您可以执行从数据库获取用户所需的查询。
【讨论】:
这正是我已经在做的事情,我已经将它们都包含在我的问题中。问题是我必须自定义它们以包含用户名和电子邮件。 您还没有提到您知道如何配置它们。你在问“有没有办法修改这两个查询来实现这一点?”。【参考方案2】:不幸的是,仅仅通过更改查询没有简单的方法来做到这一点。问题是 spring security 期望 users-by-username-query 和 authority-by-username-query 有一个参数(用户名),所以如果您的查询包含两个参数,例如
username = ? or email = ?
查询将失败。
您可以做的是实现您自己的 UserDetailsService,它将执行查询(或查询)以通过用户名或电子邮件搜索用户,然后将此实现用作 Spring Security 中的身份验证提供程序像
这样的配置 <authentication-manager>
<authentication-provider user-service-ref='myUserDetailsService'/>
</authentication-manager>
<beans:bean id="myUserDetailsService" class="xxx.yyy.UserDetailsServiceImpl">
</beans:bean>
【讨论】:
我查看了该类,但在确定应该覆盖哪些方法时遇到了一些麻烦。有什么指点吗? 我想我现在看到了:loadUserByUsername(String username)【参考方案3】:如果我理解正确,那么问题是您想在两个不同的数据库列中查找用户输入的用户名。
当然,您可以通过自定义 UserDetailsService 来做到这一点。
public class CustomJdbcDaoImpl extends JdbcDaoImpl
@Override
protected List<GrantedAuthority> loadUserAuthorities(String username)
return getJdbcTemplate().query(getAuthoritiesByUsernameQuery(), new String[] username, username, new RowMapper<GrantedAuthority>()
public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException
.......
);
@Override
protected List<UserDetails> loadUsersByUsername(String username)
return getJdbcTemplate().query(getUsersByUsernameQuery(), new String[] username, username, new RowMapper<UserDetails>()
public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException
.......
);
这个类的 bean 配置看起来像这样。
<beans:bean id="customUserDetailsService" class="com.xxx.CustomJdbcDaoImpl">
<beans:property name="dataSource" ref="dataSource"/>
<beans:property name="usersByUsernameQuery">
<beans:value> YOUR_QUERY_HERE</beans:value>
</beans:property>
<beans:property name="authoritiesByUsernameQuery">
<beans:value> YOUR_QUERY_HERE</beans:value>
</beans:property>
</beans:bean>
您的查询将与此类似
select username, password, enabled from user where (username = ? or email = ?)
select u.username, a.authority from user u join authority a on u.userId = a.userId where (username = ? or email = ?)
【讨论】:
【参考方案4】:我遇到了同样的问题,在尝试了很多不同的查询和过程之后......我发现这很有效:
public void configAuthentication(AuthenticationManagerBuilder auth)
throws Exception
// Codificación del hash
PasswordEncoder pe = new BCryptPasswordEncoder();
String userByMailQuery = "SELECT mail, password, enabled FROM user_ WHERE mail = ?;";
String userByUsernameQuery = "SELECT mail, password, enabled FROM user_ WHERE username=?";
String roleByMailQuery = "SELECT mail, authority FROM role WHERE mail =?;";
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(pe)
.usersByUsernameQuery(userByMailQuery)
.authoritiesByUsernameQuery(roleByMailQuery);
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(pe)
.usersByUsernameQuery(userByUsernameQuery)
.authoritiesByUsernameQuery(roleByMailQuery);
它只是用两个查询重复配置。
【讨论】:
【参考方案5】:这是我发现的一种解决方法。基本上我将用户名和电子邮件地址与中间的分隔符连接起来(例如'jsmith~johnsmith@gmail.com'),并检查参数是否匹配分隔符的左侧或是否匹配右侧分隔符:
select username, password, enabled
from users
where ? in (substring_index(concat(username, '~',email),'~', 1),
substring_index(concat(username, '~',email),'~', -1))
如果您担心用户名或电子邮件中可能存在分隔符(例如 ~),请改用非标准分隔符(例如 X'9C')
【讨论】:
【参考方案6】:您可以像下面的代码一样使用您的 UserDetailesService.and 配置。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userDetailsService);
关键是您不需要返回具有相同用户名的用户,您可以获得用户电子邮件并返回具有用户名的用户。代码将类似于下面的代码。
@Service
public class MyUserDetailsService implements UserDetailsService
@Override
public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException
var user = /** here search user via jpa or jdbc by username or email **/;
if(user == null )
throw new UsernameNotFoundExeption();
else return new UserDetail(user); // You can implement your user from UserDerail interface or create one;
tip* UserDetail 是一个接口,您可以创建一个或使用 Spring Default。
【讨论】:
【参考方案7】:你可以像这样配置 UserDetailesService 类。
public class UserDetailsServiceImpl implements UserDetailsService
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
User user = this.userRepository.getUserByEmailOrUserName(username); //for fetch user
if(user==null)
throw new UsernameNotFoundException("User doesn't exists");
UserDetailsImpl customUserDetails = new UserDetailsImpl(user);
return customUserDetails;
您的查询将与此类似
select * from user where email = ?或用户名 = ?
用于获取用户数据的 UserRepository 类
@Repository
public interface UserRepository extends JpaRepository<User, Integer>
@Query("from user where email = :u or username = :u")
public User getUserByEmailOrUserName(@Param("u") String username);
您也可以在登录时添加电话号码。
【讨论】:
以上是关于使用用户名或电子邮件的 Spring Security的主要内容,如果未能解决你的问题,请参考以下文章
在 Grails 的 Spring Security 插件中使用电子邮件和用户名作为登录名
Spring REST Api——访问存储库中的用户详细信息