10

我在我的 SpringMVC应用程序中使用 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 = ?

我想让它让用户可以使用用户名和电子邮件登录。有没有办法修改这两个查询来实现?还是有更好的解决方案?

4

7 回答 7

5

不幸的是,仅仅通过更改查询没有简单的方法来做到这一点。问题是 spring security 期望 users-by-username-query 和 authority-by-username-query 有一个参数(用户名),所以如果您的查询包含两个参数,例如

username = ? or email = ?

查询将失败。

您可以做的是实现您自己的UserDetailsS​​ervice,它将执行查询(或查询)以通过用户名或电子邮件搜索用户,然后将此实现用作您的 spring 安全配置中的身份验证提供程序,例如

  <authentication-manager>
    <authentication-provider user-service-ref='myUserDetailsService'/>
  </authentication-manager>

  <beans:bean id="myUserDetailsService" class="xxx.yyy.UserDetailsServiceImpl">
  </beans:bean>
于 2013-01-02T15:12:48.287 回答
5

我遇到了同样的问题,在尝试了很多不同的查询和程序之后......我发现这很有效:

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);

}

它只是用两个查询重复配置。

于 2015-10-07T08:46:09.837 回答
2

如果我理解正确,那么问题是您想在两个不同的数据库列中查找用户输入的用户名。

当然,您可以通过自定义 UserDetailsS​​ervice 来做到这一点。

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 = ?)
于 2013-01-02T15:24:09.320 回答
2

您可以像下面的代码一样使用您的 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;
    }

}

提示* UserDetail 是一个接口,您可以创建一个或使用 Spring Default。

于 2020-04-09T13:21:53.650 回答
1

<jdbc-user-service>您可以分别在tag inusers-by-username-queryauthorities-by-username-queryattributes中定义自定义查询。

<jdbc-user-service data-source-ref="" users-by-username-query="" authorities-by-username-query=""/>

更新

您可以创建实现org.springframework.security.core.userdetails.UserDetailsService和配置应用程序的类,以将其用作身份验证源。在您的自定义 UserDetails 服务中,您可以执行从数据库获取用户所需的查询。

于 2013-01-02T13:56:40.030 回答
1

您可以像这样配置 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;
    }

}

您的查询将与此类似

选择 * 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);
    
}

您还可以在登录时添加电话号码。

于 2022-01-22T07:01:43.230 回答
0

这是我发现的一种解决方法。基本上,我将用户名和电子邮件地址与中间的分隔符连接起来(例如'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')

于 2019-04-27T14:04:05.913 回答