Spring Security使用用户名或电子邮件

dluptydi  于 11个月前  发布在  Spring
关注(0)|答案(8)|浏览(119)

我在我的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 = ?


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

e37o9pze

e37o9pze1#

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

username = ? or email = ?

字符串
查询将失败。
你可以做的是实现你自己的UserDetailsService,它将执行查询(或多个查询),通过用户名或电子邮件搜索用户,然后在你的spring安全配置中使用这个实现作为身份验证提供者,比如

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

  <beans:bean id="myUserDetailsService" class="xxx.yyy.UserDetailsServiceImpl">
  </beans:bean>

lymnna71

lymnna712#

我也遇到了同样的问题,在尝试了很多不同的查询,程序.

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

}

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

xkftehaa

xkftehaa3#

如果我理解正确的话,那么问题是您希望在两个不同的DB列中查找用户输入的用户名。
当然,你可以通过自定义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 = ?)

cyej8jka

cyej8jka4#

您可以像下面的代码一样使用UserDetailesService.和config。

@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。

jw5wzhpr

jw5wzhpr5#

您可以分别在users-by-username-queryauthorities-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服务中,您可以执行从数据库获取用户所需的查询。

avkwfej4

avkwfej46#

你可以像这样配置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 =?or username =?
用于获取用户数据的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);
    
}


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

yb3bgrhw

yb3bgrhw7#

这里是我发现的一个解决方法。基本上,我将用户名和电子邮件地址连接在一起,中间有一个字符(例如'email protected(https://stackoverflow.com/cdn-cgi/l/email-protection)'),并检查参数是否匹配字符串的左侧或右侧:

select username, password, enabled 
  from users 
 where ? in (substring_index(concat(username, '~',email),'~', 1), 
             substring_index(concat(username, '~',email),'~', -1))

字符串
如果您担心用户名或电子邮件中可能会存在“”字符(例如~),请改用非标准的“”字符(例如X '9 C“)。

6fe3ivhb

6fe3ivhb8#

你可以按如下方式更改你的配置:

@Bean
    public UserDetailsService userDetailsService() {
        return input -> repository.findByUsername(input)
                .orElseGet(() -> repository.findByEmail(input)
                        .orElseThrow(() -> new UsernameNotFoundException("account not found")));
    }

字符串

相关问题