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

时间:2013-01-02 13:15:30

标签: spring authentication spring-mvc spring-security

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

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

6 个答案:

答案 0 :(得分:4)

不幸的是,仅通过更改查询就没有简单的方法。问题是spring安全性要求用户名用户名查询和用户名用户名查询都有一个参数(用户名),所以如果你的查询包含两个参数,比如

username = ? or email = ?

查询将失败。

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

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

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

答案 1 :(得分:2)

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

当然,您可以通过自定义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 = ?)

答案 2 :(得分:2)

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

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

}

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

答案 3 :(得分:1)

您可以分别在<jdbc-user-service>users-by-username-query属性的authorities-by-username-query代码中定义自定义查询。

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

<强>更新

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

答案 4 :(得分: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')

答案 5 :(得分:0)

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

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