【问题标题】:Spring Security using both username or email使用用户名或电子邮件的 Spring Security
【发布时间】: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 = ?

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

【问题讨论】:

    标签: spring authentication spring-mvc spring-security


    【解决方案1】:

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

    【讨论】:

    • 这正是我已经在做的事情,我已经将它们都包含在我的问题中。问题是我必须自定义它们以包含用户名和电子邮件。
    • 您还没有提到您知道如何配置它们。你在问“有没有办法修改这两个查询来实现这一点?”。
    【解决方案2】:

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

    username = ? or email = ?
    

    查询将失败。

    您可以做的是实现您自己的 UserDetailsS​​ervice,它将执行查询(或查询)以通过用户名或电子邮件搜索用户,然后将此实现用作 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】:

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

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

    【讨论】:

      【解决方案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);
                
            }
            

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

            【讨论】:

              猜你喜欢
              • 2017-05-20
              • 2012-12-12
              • 2013-03-15
              • 2014-05-17
              • 2014-11-18
              • 2013-10-05
              • 2012-08-19
              • 2013-05-02
              相关资源
              最近更新 更多