chapter09_保护Web应用_2_选择查询用户详细信息的服务

  • 有的时候需要对用户的信息进行存储,以便判断登录进系统时的权限。我们需要的是__用户存储__,在进行认证决策的时候,进行检索

  • Spring Security内置了了多种数据存储方式来认证用户,包括__内存__、关系型数据库、__LDAP__等

  • 使用基于内存的用户存储

    (1) 适用于开发、测试的场景,数据量小

    (2) 要重载 WebSecurityConfigurerAdapter的configure(AuthenticationManagerBuilder)方法,使用inMemoryAuthentication()方法进行基于内存的用户存储

    示例 SecurityConfig.java

      @Configuration
      @EnableWebMvcSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
         ...
         
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
              auth
                  .inMemoryAuthentication()
                  .withUser("user").password("password").roles("USER").
                  and.withUser("admin").password("password").roles("USER", "ADMIN");
          }
      }
    

    (3) withUser方法添加新的用户,返回类型为 UserDetailsManagerConfigurer,这个对象可以进一步做一系列的用户信息细节配置,详见P259

    (4) 使用and()方法可以继续添加新用户

  • 基于RDBMS的用户存储与认证

    (1) 示例 SecurityConfig.java

      import javax.sql.DataSource;
    
      @Configuration
      @EnableWebMvcSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
          ...
    
          @Autowired
          DataSource dataSource;
    
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
    
              auth.jdbcAuthentication().dataSource(dataSource).
                      usersByUsernameQuery("select username, password, true from Spitter " + "where username=?").
                      authoritiesByUsernameQuery("select username, 'ROLE_USER' from Spitter " + "where username=?").
                      passwordEncoder(new StandardPasswordEncoder());
          }
      }
    

    (2) 使用jdbcAuthentication()方法可以配置RDBMS型的用户存储,我们只需要自动装配@Autowired一个dataSource即可

    (3) 当直接使用

      auth.jdbcAuthentication().dataSource(dataSource);
    

    时,会采用默认的SQL语句,在SpringSecurity内置的JdbcDaoImpl类中

      public static final String DEF_USERS_BY_USERNAME_QUERY =
          "select username,password,enabled " +
          "from users " +
          "where username = ?";
      public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
          "select username,authority " +
          "from authorities " +
          "where username = ?";
      public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
          "select g.id, g.group_name, ga.authority " +
          "from groups g, group_members gm, group_authorities ga " +
          "where gm.username = ? " +
          "and g.id = ga.group_id " +
          "and g.id = gm.group_id";
    

    但是,当数据库和默认的表名等不统一时,需要重写和以上三个SQL语句关联的方法;

    其中, usersByUsernameQuery()方法(认证查询)和DEF_USERS_BY_USERNAME_QUERY关联;authoritiesByUsernameQuery()方法(权限查询)和DEF_AUTHORITIES_BY_USERNAME_QUERY关联;groupAuthoritiesByUsername()方法(群组权限查询)和DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY关联

    (4) 以上的3个方法在自己配置时,要按照规定的协议来配置:

    所有的查询都将用户名username作为唯一的参数;

    认证查询会选取用户名、密码、启用状态;

    权限查询会选取用户名、权限信息;

    群组权限查询会选取群组id、群组名称、权限;

    即使数据库中包含上面要查询的某项,也要使用布尔值或字符串的方式进行填充(类似于"select username, password, true from Spitter where username=?“和"select username, ‘ROLE_USER’ from Spitter where username=?”)

    (5) 使用转码后的密码

    用户的密码应该进行加密,然后存储在数据库中,使用passwordEncoder()方法就可以解决这个问题;

    数据库中的密码永远不会被解码,而是用户登录时输入的密码使用相同的算法进行加密,然后和数据库中转码后的密码进行对比

猜你喜欢

转载自blog.csdn.net/captxb/article/details/87884580
今日推荐