Spring Security-自定义用户服务信息

正如之前论述的,在WebSecurityConfigurerAdapter中的方法

    /**
     * 用来配置用户签名服务,主要是user-details机制,你还可以给予用户赋予角色
     * @param auth 签名管理器构造器,用于用户具体权限控制
     * @throws Exception
     */
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

这是一个用于配置用户信息的方法,在Spring Security中默认是没有任何用户配置的.而在Spring Boot中如果没有用户的配置,他会自动生成一个名称为user,密码通过随机生成的用户,密码可以在日志中观察中得到.但是这样就存在各类的弊端.
为了克服这类弊端,这里先讨论,这里先讨论如何进行自定义用户签名服务.这里主要包括使用内存签名服务,数据库签名服务,自定义签名服务.关于如何限定请求权限,是通过WebSecurityConfigurerAdapter中的方法configure(HttpSecurity http)来实现的.只是这里在默认的情况下,所有的请求一旦通过验证就会得到放行.在这一节李,暂时不讨论不同用户不同权限的问题,而是仅仅讨论如何验证用户和赋予用户角色的问题.

1. 使用内存签名服务

从标题来看,顾名思义就是将用户的信息存放到内存中.相对而言,他比较简单,适合测试
快速搭建环境,下面下举一个例子.

@Configuration
public class MyWebMvcConfig extends WebSecurityConfigurerAdapter {
    /**
     * 用来配置用户签名服务,主要是user-details机制,你还可以给予用户赋予角色
     * @param auth 签名管理器构造器,用于用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        //使用内存存储
        auth.inMemoryAuthentication()
                //设置密码编辑器
                .passwordEncoder(passwordEncoder)
                //注册用户admin,密码为abc,并且赋予USER和ADMIN权限
                .withUser("admin")
                //可以通过passwordEncoder.encode("123456")得到加密的密码
                .password("$2a$10$Q/NH6dHW7.oESznV6F22vOL1Y3kf2GlLmmFDUKaol7/ASLwI3y4cm")
                //权限
                .roles("USER", "ADMIN")
                //链接方法and
                .and()
                .withUser("myuser")
                .password("$2a$10$Q/NH6dHW7.oESznV6F22vOL1Y3kf2GlLmmFDUKaol7/ASLwI3y4cm")
                .roles("USER");

    }

在Spring5的Security中都要求使用密码编码器,否则会发生异常,所以代码中首先创建了一个BCryptPasswordEncoder实例,这个类实现了PasswordEncoder接口,它采用的是单向不可逆的密码的加密方式.这里的AuthentictionManagerBuilder是关注的焦点,其中的inMemoryAuthentication方法将返回内存保存用户信息的管理器配置(InMemoryUSerDetailsManagerConfigurer),这样启动内存缓存的机制保存用户信息.首先通过passwordEncdoer方法,设置了密码编码器,这里的withUSer方法是注册用户名称,返回用户详情构造器(UserDetailsBuilder)对象,这样就可以去配置用户的信息了.

password方法是设置密码,采用的是通过Bcrypt加密方式加密后的密码字符串,于是用户登录就需要这个密码了;

roles方法赋予角色类型,将来可以通过这个角色名称赋予权限了.只是这个roles方法还有内涵,他实际是另一个方法的简写,这个方法是authorities,使用他可以注册角色名称,而代码中roles方法给的角色实际Spring Security会加入前缀"ROLE_";

and方法则是一个链接方法,也就是开启另一个用户的注册.通过configure(AuthenticationManagerBuilder auth)方法,可以注册两个用户.一个是admin用户,其密码是123456,他拥有ROLE_USER和ROLE_ADMIN两个角色;另一个是myuser用户,器密码是123456,他只拥有ROLE_USER一个角色.
上面的方法中,使用and作为连接.有时候会显得比较冗余,于是我们修改代码中的config方法,如下

    /**
     * 用来配置用户签名服务,主要是user-details机制,你还可以给予用户赋予角色
     * @param auth 签名管理器构造器,用于用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        //使用内存存储
        InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> userConfig = auth.inMemoryAuthentication()
                //设置内存编码器
                .passwordEncoder(passwordEncoder);
        //注册用户admin,密码123456,并赋予USER和ADMIN权限
        userConfig.withUser("admin")
                .password("$2a$10$Q/NH6dHW7.oESznV6F22vOL1Y3kf2GlLmmFDUKaol7/ASLwI3y4cm")
                .authorities("ROLE_USER","ROLE_ADMIN");
        //注册用户myuser,密码123456,并赋予USER权限
        userConfig.withUser("myuser")
                .password("$2a$10$Q/NH6dHW7.oESznV6F22vOL1Y3kf2GlLmmFDUKaol7/ASLwI3y4cm")
                .authorities("ROLE_USER");

    }

这样会清爽一些,还是实现与代码清单相同的功能,只是这里将roles方法改成了authorities方法,所以多加了前缀"ROLE_".但是无论如何,使用内存缓存用户信息的方式都不是主要的方式,毕竟内存空间有限,而且会占用JVM的内存空间,不过在开发和测试阶段使用这样的方式可以满足快速开发和测试的需求
内存用户的使用方法就介绍到这里.因为用户详情构造器(UserDetailsBuilder)后面还会经常用到,所以给出表来介绍他的其他方法的使用.
Spring Security-自定义用户服务信息_第1张图片

2. 使用数据库自定义用户认证服务

在大部分情况下,用户信息会存放到数据库,为此Spring Security提供了对数据库的查询方式来满足开发者的需要.JdbcUSerDetailsManagerConfiguere是一个Spring Security对于数据库配置的支持,并且他也提供了默认的SQL.只是在大部分的情况腺癌,不会采用他默认提供的SQL而是,基于使用的原则,这里不再深入讨论默认的SQL了,既然设计到了数据库,就要准备好数据库(MySQL)的表和数据,代码清单如下
Spring Security-自定义用户服务信息_第2张图片

读者可以在数据库表中插入对应的数据,然后使用Spring Security提供的数据库权限进行验证.下面的代码是实例

@Configuration
public class MyWebMvcConfig extends WebSecurityConfigurerAdapter {
    //注入数据源
    @Autowired
    private DataSource dataSource;
    //使用用户名查询密码
    String pwdQuery = "select user_name, pwd, avaliable " +
            "from t_user where user_name=?";
    //使用用户名查询角色信息
    String roleQuery = "select u.user_name, r.role_name " +
            "from t_user u, t_user_role ur, t_role r " +
            "where u.id = ur.user_id and r.id = ur.role_id " +
            "and u.user_name=?";
    /**
     * 用来配置用户签名服务,主要是user-details机制,你还可以给予用户赋予角色
     * @param auth 签名管理器构造器,用于用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        auth.jdbcAuthentication()
        //密码编码器
                .passwordEncoder(passwordEncoder)
                //数据源
                .dataSource(dataSource)
                //查询用户,判断密码是否一致
                .usersByUsernameQuery(pwdQuery)
                //赋予权限
                .authoritiesByUsernameQuery(roleQuery);

    }

代码中首先使用@Autowired注入了数据源,这样就可以使用SQL.其次是定义了两条SQL,其中pwdQuery所定义的是根据用户名查询用户信息,roleQuery是使用角色名去查询角色信息,这样就能够赋予角色.
然后看到config方法,使用了AuthenticationManagerBuilder的jdbcAuthentication方法,这样就可以启用JDBC的方式进行验证了.passwordEncoder方法则是设置密阿妈解码器,然后使用dataSource方法绑定注入的数据源,接着就是usersByUsernameQuery方法,他通过pwdQuery所定义的SQL返回三个列,分别是用户名,密码和布尔值.这样就对用户名和密码进行验证了.其中布尔值是判断用户是否有效,这里返回的是available列,他存储的数据已经被约束为1和0,如果为1则用户有效,否则无效.而authoritiesByUsernameQuery方法会用roleQuery定义的SQL,通过用户名称查询角色名称,这样Spring Security就会根据查询的结果赋予权限.值得注意的是,如果这条SQL返回多条,那么就会给这个用户赋予多个角色.

但是上述代码存在一定弊端,虽然通过BCrypt加密的密文很难破解,但是仍旧不能避免用户使用类似"12345"这样简单的密码,如果被人截取了这些简单的密码,进行匹配,那么一些用户的密码就有可能被别人破译.为了克服这些问题,在实际的企业生产中还可能通过自己的阴匙对密码进行加密处理,而阴匙存在企业服务器上,这样即使被人截取了这些简单的密码,别人也无法得到密钥破解密文,这样就能够大大的提高网站的安全性.对此Spring Security也进行了支持,只需要使用密码编码器(Pbkdf2PasswordEncoder类)对象即可.这里我们现在secret.properties中加入一个属性:

system.user.password.secret=uvwxyz

这是一个钥匙,只有拿到这个钥匙才能够通过加密算法对密码进行匹配,这样破解的难度就大大增加了,为了能够更加安全的保护密码信息.然狗对代码清单中的部分代码进行改造.

@Configuration
@PropertySource(value = "classpath:secret.properties")
public class MyWebMvcConfig extends WebSecurityConfigurerAdapter {
    //注入数据源
    @Autowired
    private DataSource dataSource;
    @Value("${system.user.password.secret}")
    private String secret = null;
    //使用用户名查询密码
    String pwdQuery = "select user_name, pwd, avaliable " +
            "from t_user where user_name=?";
    //使用用户名查询角色信息
    String roleQuery = "select u.user_name, r.role_name " +
            "from t_user u, t_user_role ur, t_role r " +
            "where u.id = ur.user_id and r.id = ur.role_id " +
            "and u.user_name=?";
    /**
     * 用来配置用户签名服务,主要是user-details机制,你还可以给予用户赋予角色
     * @param auth 签名管理器构造器,用于用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder(this.secret);
        auth.jdbcAuthentication()
        //密码编码器
                .passwordEncoder(passwordEncoder)
                //数据源
                .dataSource(dataSource)
                //查询用户,判断密码是否一致
                .usersByUsernameQuery(pwdQuery)
                //赋予权限
                .authoritiesByUsernameQuery(roleQuery);

    }

在这段代码中,使用了Pbkd2PasswordEncoder创建密码编码器(PasswordEncoder).实际上,Spring Security还存在SCryptPasswordEncoder和DelegatingpassEncoder等密码加载器,用户可以根据自己的需要去创建不同的密码编码器,甚至可以自己实现密码编码器(PasswordEncoder)接口,定义自己的编码器.这样密码就更加安全了.

3. 使用自定义用户认证服务

假设系统已经提供了UserRole接口,通过他可以操作数据库,下面基于这个基础进行开发.首先设置用户权限的方式,对于Spring Security提供了一个UserDetailsService接口,通过他可以获取用户信息,而这个接口只有一个loadUserByUsername方法需要实现,这个方法定义返回UserDetails接口对象,于是很快可以通过类似于一下代码来实现这个接口,进而获取用户信息

package cn.hctech2006.boot.bootsecurity.security;

import java.util.ArrayList;
import java.util.List;

public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRoleServiceImpl userRoleService;
    @Override
    @Transactional
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //获取数据库用户信息
        DatabaseUser dbUser =  userRoleService.getUserByName(s);
        //获取数据库角色信息
        List<DatabaseRole> dbRole = userRoleService.findRolesByUserName(s);
        //赋予查到的角色
        return changeToUser(dbUser,dbRole);

    }
    private UserDetails changeToUser(DatabaseUser dbUser, List<DatabaseRole> roleList){
        //权限列表
        List<GrantedAuthority> authorityList = new ArrayList<>();
        //赋予查到的角色
        for(DatabaseRole role : roleList){
            GrantedAuthority authority = new SimpleGrantedAuthority(role.getName());
            authorityList.add(authority);
        }
        //创建UserDetails对象,设置用户名,密码和权限
        UserDetails userDetails = new User(dbUser.getName(),dbUser.getPassword(),authorityList);
        return userDetails;
    }
}

把这个类标注为@Service,这样Spring就可以自东的扫描他为Bean,然后通过自动注入了UserRoleService这接口.接着是覆盖接口的loadUserByUsername方法,在这个方法中先通过UserRoleService接口查询到用户和角色的相关信息,然后通过changToUser的构造哦方法把用户名称和密码传递,这样用户详情(UserDetails)就拥有了这些信息.
然后我们需要认证服务注册这个UserDetailsServiceImpl

@Configuration
@PropertySource(value = "classpath:secret.properties")
public class MyWebMvcConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsService userDetailsService;

    /**
     * 用来配置用户签名服务,主要是user-details机制,你还可以给予用户赋予角色
     * @param auth 签名管理器构造器,用于用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder(this.secret);
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);

    }

你可能感兴趣的:(Spring Security-自定义用户服务信息)